/*
 *	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.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ultrasound.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <math.h>
#include <string.h>

#define CMD_ARPEG		0x00
#define CMD_SLIDEUP		0x01
#define CMD_SLIDEDOWN		0x02
#define CMD_SLIDETO		0x03
#define    SLIDE_SIZE		8
#define CMD_VOLSLIDE		0x0a
#define CMD_JUMP		0x0b
#define CMD_VOLUME		0x0c
#define CMD_BREAK		0x0d
#define CMD_SPEED		0x0f
#define CMD_NOP			0xfe
#define CMD_NONOTE		0xff

#define MIN(a, b)		((a) < (b) ? (a) : (b))

#define MAX_TRACK	8
#define MAX_PATTERN	128
#define MAX_POSITION	128

struct note_info
  {
    unsigned char   note;
    unsigned char   vol;
    unsigned char   sample;
    unsigned char   command;
    short           parm1, parm2;
  };

struct voice_info
  {
    int             sample;
    int             note;
    int             volume;
    int             pitchbender;

    /* Pitch sliding */

    int             slide_pitch;
    int             slide_goal;
    int             slide_rate;

    int             volslide;
  };

typedef struct note_info pattern[MAX_TRACK][64];
int             pattern_len[MAX_POSITION];
int             pattern_tempo[MAX_POSITION];
pattern        *pattern_table[MAX_PATTERN];

struct voice_info voices[MAX_TRACK];

int             nr_channels, nr_patterns, songlength;
int             tune[MAX_POSITION];
double          tick_duration;

int             period_table[] =
{
  856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
  428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
  214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113
};

SEQ_DEFINEBUF (2048);

int             seqfd;
int             sample_ok[128], sample_vol[128];
int             tmp, gus_dev;
double          this_time, next_time;
int             ticks_per_division;
double          clock_rate;	/* HZ */

/*
 * The function seqbuf_dump() must always be provided
 */

void            play_module (char *name);
int             load_module (char *name);
int             play_note (int channel, struct note_info *pat);
void            lets_play_voice (int channel, struct voice_info *v);

void
seqbuf_dump ()
{
  if (_seqbufptr)
    if (write (seqfd, _seqbuf, _seqbufptr) == -1)
      {
	perror ("write /dev/sequencer");
	exit (-1);
      }
  _seqbufptr = 0;
}

void
init_voices ()
{
  int             i;

  for (i = 0; i < MAX_TRACK; i++)
    {
      voices[i].sample = 0;
      voices[i].note = 0;
      voices[i].volume = 64;

      voices[i].slide_pitch = 0;
      voices[i].slide_goal = 0;
      voices[i].slide_rate = 0;
      voices[i].pitchbender = 0;

      voices[i].volslide = 0;
    }
}

int
main (int argc, char *argv[])
{
  int             i, n, j;
  struct synth_info info;

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

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

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

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

      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 (-1);
    }

  GUS_NUMVOICES (gus_dev, 14);

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

      if (load_module (argv[i]))
	{
	  tick_duration = 100.0 / clock_rate;
	  play_module (argv[i]);
	}

    }

  SEQ_DUMPBUF ();
  close (seqfd);

  exit (0);
}

unsigned short
intelize (unsigned short v)
{
  return ((v & 0xff) << 8) | ((v >> 8) & 0xff);
}

unsigned long
intelize4 (unsigned long v)
{
  return
  (((v >> 16) & 0xff) << 8) | (((v >> 16) >> 8) & 0xff) |
  (((v & 0xff) << 8) | ((v >> 8) & 0xff) << 16);
}

int
load_stm_module (int mod_fd, char *name)
{

  struct sample_header
    {
      char            name[12];
      unsigned char   instr_disk;
      unsigned short  reserved1;
      unsigned short  length;	/* In bytes */
      unsigned short  loop_start;
      unsigned short  loop_end;
      unsigned char   volume;
      unsigned char   reserved2;
      unsigned short  C2_speed;
      unsigned short  reserved3;

    };

  int             i, total_mem;
  int             sample_ptr;

  int             position;

  unsigned char  *tune_ptr;	/* array 0-127 */

  char            header[1105], sname[21];

  int             nr_samples;	/* 16 or 32 samples (or 64 or ???) */
  int             slen, npat;

  fprintf (stderr, "Loading .STM module: %s\n", name);

  if (read (mod_fd, header, sizeof (header)) != sizeof (header))
    {
      fprintf (stderr, "%s: Short file (header)\n", name);
      close (mod_fd);
      return 0;
    }

  strncpy (sname, header, 20);

  fprintf (stderr, "\nModule: %s - ", sname);

  if (header[28] != 0x1a)
    {
      fprintf (stderr, "Not a STM module\n");
      close (mod_fd);
      return 0;
    }

  npat = header[33];
  slen = 0;
  tune_ptr = &header[48 + (31 * 32)];

  for (i = 0; i < 64; i++)
    {
      tune[i] = tune_ptr[i];
      if (tune[i] < npat)
	slen = i;
    }

  fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat);

  nr_samples = 31;

  sample_ptr = 48 + (31 * 32) + 64 + (npat * 1024);	/* Location where the
							 * first sample is
							 * stored */
  total_mem = 0;

  for (i = 0; i < 32; i++)
    sample_ok[i] = 0;

  for (i = 0; i < nr_samples; i++)
    {
      int             len, loop_start, loop_end, base_freq;
      unsigned short  loop_flags = 0;

      struct sample_header *sample;

      struct patch_info *patch;

      sample = (struct sample_header *) &header[48 + (i * 32)];

      len = sample->length;
      loop_start = sample->loop_start;
      loop_end = sample->loop_end;
      base_freq = sample->C2_speed;

      if (strlen (sample->name) > 21)
	{
	  fprintf (stderr, "\nInvalid name for sample #%d\n", i);
	  close (mod_fd);
	  return 0;
	}

      if (len > 0)
	{
	  int             x;

	  if (loop_end > len)
	    loop_end = 1;
	  else if (loop_end < loop_start)
	    {
	      loop_start = 0;
	      loop_end = 0;
	    }
	  else
	    loop_flags = WAVE_LOOPING;

	  total_mem += len;
	  patch = (struct patch_info *) malloc (sizeof (*patch) + len);

	  patch->key = GUS_PATCH;
	  patch->device_no = gus_dev;
	  patch->instr_no = i;
	  patch->mode = loop_flags;
	  patch->len = len;
	  patch->loop_start = loop_start;
	  patch->loop_end = loop_end;
	  patch->base_freq = base_freq;
	  patch->base_note = 261630;	/* Mid C */
	  patch->low_note = 0;
	  patch->high_note = 0x7fffffff;
	  patch->volume = 120;

	  if (lseek (mod_fd, sample_ptr, 0) == -1)
	    {
	      perror (name);
	      close (mod_fd);
	      free (patch);
	      return 0;
	    }

	  sample_ptr += len;

	  if ((x = read (mod_fd, patch->data, len)) != len)
	    {
	      fprintf (stderr, "Short file (sample at %d (%d!=%d)\n", sample_ptr, x, len);
	      close (mod_fd);
	      free (patch);
	      return 0;
	    }

	  fprintf (stderr, "Sample %02d: %05d, %05d, %05d, %07d %s\n",
		   i,
		   len,
		   loop_start,
		   loop_end,
		   base_freq,
		   sample->name);

	  if (write (seqfd, patch, sizeof (*patch) + len) == -1)
	    {
	      perror ("ioctl /dev/sequencer");
	      exit (-1);
	    }
	  else
	    sample_ok[i] = 1;

	  free (patch);
	}
    }

  nr_patterns = slen;
  songlength = slen;
  nr_channels = 4;

  for (position = 0; position < npat; position++)
    {
      unsigned char   patterns[64][4][4];
      int             pat, channel, x;

      int             pp = 1104 + (position * 1024);

      if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
	{
	  fprintf (stderr, "Can't allocate memory for a pattern\n");
	  return 0;
	}

      if (lseek (mod_fd, pp, 0) == -1)
	{
	  perror (name);
	  close (mod_fd);
	  return 0;
	}

      if ((x = read (mod_fd, patterns, 1024)) != 1024)
	{
	  fprintf (stderr, "Short file (pattern at %d), %d!=%d\n", pp, x, 1024);
	  close (mod_fd);
	  return 0;
	}

      for (pat = 0; pat < 64; pat++)
	{

	  for (channel = 0; channel < 4; channel++)
	    {
	      unsigned char  *p;

	      unsigned        vol, note, octave, sample, effect, params;

	      p = &patterns[pat][channel][0];

	      if (p[0] < 251)
		{
		  note = p[0] & 15;
		  octave = p[0] / 16;

		  note = 48 + octave * 12 + note;

		  sample = p[1] / 8;
		  vol = (p[1] & 7) + (p[2] / 2);
		  effect = p[2] & 0xF;
		  params = p[3];
		}
	      else
		{
		  note = 0;
		  octave = 0;

		  sample = 0;
		  vol = 0;
		  effect = CMD_NONOTE;
		  params = 0;
		}

	      (*pattern_table[position])[channel][pat].note = note;
	      (*pattern_table[position])[channel][pat].sample = sample;
	      (*pattern_table[position])[channel][pat].command = effect;
	      (*pattern_table[position])[channel][pat].parm1 = params;
	      (*pattern_table[position])[channel][pat].parm2 = 0;
	      (*pattern_table[position])[channel][pat].vol = vol;
	    }

	}

    }

  close (mod_fd);
  return 1;
}

int
load_669_module (int mod_fd, char *name)
{
  struct sample_header
    {
      char            name[13];
      unsigned long   length;	/* In bytes */
      unsigned long   loop_start;
      unsigned long   loop_end;
    };

  int             i, total_mem;
  int             sample_ptr;

  int             position;

  unsigned char  *tune_ptr, *len_ptr, *tempo_ptr;	/* array 0-127 */

  char            header[1084];
  char            msg[110];

  int             nr_samples;	/* 16 or 32 samples */
  int             slen, npat;

  clock_rate = 25.0;

  fprintf (stderr, "Loading .669 module: %s\n", name);

  if (read (mod_fd, header, sizeof (header)) != sizeof (header))
    {
      fprintf (stderr, "%s: Short file (header)\n", name);
      close (mod_fd);
      return 0;
    }

  if (*(unsigned short *) &header[0] != 0x6669)
    {
      fprintf (stderr, "Not a 669 file\n");
      close (mod_fd);
      return 0;
    }

  strncpy (msg, &header[2], 108);

  for (i = 0; i < strlen (msg); i++)
    if ((msg[i] >= ' ' && msg[i] <= 'z') || msg[i] == '\n')
      printf ("%c", msg[i]);
  printf ("\n");

  npat = header[0x6f];

  tune_ptr = &header[0x71];

  for (slen = 0; slen < 128 && tune_ptr[slen] != 0xff; slen++);
  slen--;

  for (i = 0; i < slen; i++)
    tune[i] = tune_ptr[i];

  len_ptr = &header[0x171];
  for (i = 0; i < slen; i++)
    pattern_len[i] = len_ptr[i] - 1;

  tempo_ptr = &header[0xf1];
  for (i = 0; i < slen; i++)
    pattern_tempo[i] = tempo_ptr[i];

  nr_samples = header[0x6e];

  fprintf (stderr, "Song lenght %d, %d patterns, %d samples.\n", slen, npat, nr_samples);

  sample_ptr = 0x1f1 + (nr_samples * 0x19) + (npat * 0x600);	/* Location where the
								 * first sample is
								 * stored */
  total_mem = 0;

  for (i = 0; i < 64; i++)
    sample_ok[i] = 0;

  for (i = 0; i < nr_samples; i++)
    {
      int             len, loop_start, loop_end;
      unsigned short  loop_flags = 0;

      struct sample_header *sample;
      char            sname[14];

      struct patch_info *patch;

      sample = (struct sample_header *) &header[0x1f1 + (i * 0x19)];

      len = *(unsigned long *) &sample->name[13];
      loop_start = *(unsigned long *) &sample->name[17];
      loop_end = *(unsigned long *) &sample->name[21];
      if (loop_end > len)
	loop_end = 1;
      else if (loop_end == len)
	loop_end--;

      if (loop_end < loop_start)
	{
	  loop_start = 0;
	  loop_end = 0;
	}

      strncpy (sname, sample->name, 13);

      if (len > 0 && len < 200000)
	{
	  total_mem += len;

	  fprintf (stderr, "Sample %02d: %05d, %05d, %05d   %s\n",
		   i,
		   len,
		   loop_start,
		   loop_end,
		   sname);

	  patch = (struct patch_info *) malloc (sizeof (*patch) + len);

	  if (loop_end == 0)
	    loop_end = 1;
	  if (loop_end >= len)
	    loop_end = 1;

	  if (loop_end > 1) loop_flags = WAVE_LOOPING;

	  patch->key = GUS_PATCH;
	  patch->device_no = gus_dev;
	  patch->instr_no = i;
	  patch->mode = WAVE_UNSIGNED | loop_flags;
	  patch->len = len;
	  patch->loop_start = loop_start;
	  patch->loop_end = loop_end;
	  patch->base_freq = 8448;
	  patch->base_note = 261630;
	  patch->low_note = 1000;
	  patch->high_note = 0x7fffffff;
	  patch->volume = 120;

	  if (lseek (mod_fd, sample_ptr, 0) == -1)
	    {
	      fprintf (stderr, "Seek failed\n");
	      perror (name);
	      close (mod_fd);
	      free (patch);
	      return 0;
	    }

	  sample_ptr += len;

	  if (read (mod_fd, patch->data, len) != len)
	    {
	      fprintf (stderr, "Short file (sample at %d)\n", sample_ptr);
	      close (mod_fd);
	      free (patch);
	      return 0;
	    }

	  if (write (seqfd, patch, sizeof (*patch) + len) == -1)
	    {
	      perror ("ioctl /dev/sequencer");
	      /* exit (-1);	 */
	    }
	  else
	    sample_ok[i] = 1;

	  free (patch);
	}
    }

  nr_patterns = slen;
  songlength = slen;
  nr_channels = 8;

  for (position = 0; position < npat; position++)
    {
      unsigned char   patterns[0x600];
      int             pat, channel, x;

      int             pp = 0x1f1 + (nr_samples * 0x19) + (position * 0x600);

      if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
	{
	  fprintf (stderr, "Can't allocate memory for a pattern\n");
	  return 0;
	}


      if (lseek (mod_fd, pp, 0) == -1)
	{
	  perror (name);
	  close (mod_fd);
	  return 0;
	}

      if ((x = read (mod_fd, patterns, 1024)) != 1024)
	{
	  fprintf (stderr, "Short file (pattern at %d) %d!=1024\n", pp, x);
	  close (mod_fd);
	  return 0;
	}

      for (pat = 0; pat < 64; pat++)
	{

	  for (channel = 0; channel < 8; channel++)
	    {
	      unsigned char  *p;

	      unsigned        vol, period, sample, effect, params;

	      p = &patterns[pat * 24 + channel * 3];

	      if (p[0] >= 0xfe ||
		  (p[0] == 0xff && p[1] == 0xff && p[2] == 0xff) ||
		  (p[0] == 0 && p[1] == 0 && p[2] == 0) ||
		  *(int *) p == -1)
		{
		  period = 0;
		  effect = CMD_NONOTE;
		  sample = 0;
		  vol = 0;
		  params = 0;

		  if (p[0] == 0)
		    {
		      effect = CMD_BREAK;
		      params = -2;
		    }
		}
	      else
		{
		  period = (p[0] >> 2) + 48;
		  effect = (p[2] >> 4);
		  params = p[2] & 0x0f;
		  vol = p[1] & 0x0f;

		  if (p[2] == 0xfe)
		    {
		      effect = CMD_VOLUME;
		      params = vol;
		    }
		  else if (p[2] == 0xff)
		    {
		      effect = CMD_NOP;
		    }
		  else
		    switch (effect)
		      {
		      case 0:	/* a - Portamento up */
			effect = CMD_SLIDEUP;
			break;

		      case 1:	/* b - Portamento down */
			effect = CMD_SLIDEDOWN;
			break;

		      case 2:	/* c - Port to note */
			effect = CMD_SLIDETO;
			break;

		      case 3:	/* d - Frequency adjust */
			effect = CMD_NOP;	/* To be implemented */
			break;

		      case 4:	/* e - Frequency vibrato */
			effect = CMD_NOP;	/* To be implemented */
			break;

		      case 5:	/* f - Set tempo */
			effect = CMD_SPEED;
			break;

		      default:
			effect = CMD_NOP;
		      }

		  sample = (((p[0] << 4) & 0x30) | ((p[1] >> 4) & 0x0f)) + 1;
		}

	      (*pattern_table[position])[channel][pat].note = period;
	      (*pattern_table[position])[channel][pat].sample = sample;
	      (*pattern_table[position])[channel][pat].command = effect;
	      (*pattern_table[position])[channel][pat].parm1 = params;
	      (*pattern_table[position])[channel][pat].parm2 = 0;
	      (*pattern_table[position])[channel][pat].vol = vol;
	    }

	}

    }

  close (mod_fd);
  return 1;
}

int
load_mmd0_module (int mod_fd, char *name)
{

  struct sample_header
    {
      unsigned short  loop_start;
      unsigned short  loop_end;
      unsigned char   midich;
      unsigned char   midipreset;
      unsigned char   volume;
      unsigned char   strans;
    };

  int             i, total_mem;
  int             sample_ptr;

  int             position;

  unsigned char  *tune_ptr;	/* array 0-127 */

  char            header[1105];

  int             nr_samples;	/* 16 or 32 samples (or 64 or ???) */
  int             slen, npat;

  fprintf (stderr, "Loading .MED module: %s\n", name);

  if (read (mod_fd, header, sizeof (header)) != sizeof (header))
    {
      fprintf (stderr, "%s: Short file (header)\n", name);
      close (mod_fd);
      return 0;
    }

  if (strncmp (header, "MMD0", 4))
    {
      fprintf (stderr, "Not a MED module\n");
      close (mod_fd);
      return 0;
    }

  printf ("Module len %d\n", intelize4 (*(long *) &header[4]));
  printf ("Song info %d\n", intelize4 (*(long *) &header[8]));
  printf ("Song len %d\n", intelize4 (*(long *) &header[12]));
  printf ("Blockarr %x\n", intelize4 (*(long *) &header[16]));
  printf ("Blockarr len %d\n", intelize4 (*(long *) &header[20]));
  printf ("Sample array %x\n", intelize4 (*(long *) &header[24]));
  printf ("Sample array len %d\n", intelize4 (*(long *) &header[28]));
  printf ("Exp data %x\n", intelize4 (*(long *) &header[32]));
  printf ("Exp size %d\n", intelize4 (*(long *) &header[36]));
  printf ("Pstate %d\n", intelize (*(long *) &header[40]));
  printf ("Pblock %d\n", intelize (*(long *) &header[42]));

  return 0;

  npat = header[33];
  slen = 0;
  tune_ptr = &header[48 + (31 * 32)];

  for (i = 0; i < 64; i++)
    {
      tune[i] = tune_ptr[i];
      if (tune[i] < npat)
	slen = i;
    }

  fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat);

  nr_samples = 31;

  sample_ptr = 48 + (31 * 32) + 64 + (npat * 1024);	/* Location where the
							 * first sample is
							 * stored */
  total_mem = 0;

  for (i = 0; i < 32; i++)
    sample_ok[i] = 0;

  for (i = 0; i < nr_samples; i++)
    {
      int             len, loop_start, loop_end, base_freq;
      unsigned short  loop_flags = 0;

      struct sample_header *sample;

      struct patch_info *patch;

      sample = (struct sample_header *) &header[48 + (i * 32)];

      /*
       * len = sample->length; loop_start = sample->loop_start; loop_end =
       * sample->loop_end; base_freq = sample->C2_speed;
       * 
       * if (strlen (sample->name) > 21) { fprintf (stderr, "\nInvalid name for
       * sample #%d\n", i); close (mod_fd); return 0; }
       */
      if (len > 0)
	{
	  int             x;

	  if (loop_end > len)
	    loop_end = 1;

	  if (loop_end < loop_start)
	    {
	      loop_start = 0;
	      loop_end = 0;
	    }

	  if (loop_end > 2) loop_flags = WAVE_LOOPING;

	  total_mem += len;
	  patch = (struct patch_info *) malloc (sizeof (*patch) + len);

	  patch->key = GUS_PATCH;
	  patch->device_no = gus_dev;
	  patch->instr_no = i;
	  patch->mode = loop_flags;
	  patch->len = len;
	  patch->loop_start = loop_start;
	  patch->loop_end = loop_end;
	  patch->base_freq = base_freq;
	  patch->base_note = 261630;	/* Mid C */
	  patch->low_note = 0;
	  patch->high_note = 0x7fffffff;
	  patch->volume = 120;

	  if (lseek (mod_fd, sample_ptr, 0) == -1)
	    {
	      perror (name);
	      close (mod_fd);
	      free (patch);
	      return 0;
	    }

	  sample_ptr += len;

	  if ((x = read (mod_fd, patch->data, len)) != len)
	    {
	      fprintf (stderr, "Short file (sample at %d (%d!=%d)\n", sample_ptr, x, len);
	      close (mod_fd);
	      free (patch);
	      return 0;
	    }
	  /*
	   * fprintf (stderr, "Sample %02d: %05d, %05d, %05d, %07d %s\n", i,
	   * len, loop_start, loop_end, base_freq, sample->name);
	   */
	  if (write (seqfd, patch, sizeof (*patch) + len) == -1)
	    {
	      perror ("ioctl /dev/sequencer");
	      exit (-1);
	    }
	  else
	    sample_ok[i] = 1;

	  free (patch);
	}
    }

  nr_patterns = slen;
  songlength = slen;
  nr_channels = 4;

  for (position = 0; position < npat; position++)
    {
      unsigned char   patterns[64][4][4];
      int             pat, channel, x;

      int             pp = 1104 + (position * 1024);

      if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
	{
	  fprintf (stderr, "Can't allocate memory for a pattern\n");
	  return 0;
	}

      if (lseek (mod_fd, pp, 0) == -1)
	{
	  perror (name);
	  close (mod_fd);
	  return 0;
	}

      if ((x = read (mod_fd, patterns, 1024)) != 1024)
	{
	  fprintf (stderr, "Short file (pattern at %d), %d!=%d\n", pp, x, 1024);
	  close (mod_fd);
	  return 0;
	}

      for (pat = 0; pat < 64; pat++)
	{

	  for (channel = 0; channel < 4; channel++)
	    {
	      unsigned char  *p;

	      unsigned        vol, note, octave, sample, effect, params;

	      p = &patterns[pat][channel][0];

	      if (p[0] < 251)
		{
		  note = p[0] & 15;
		  octave = p[0] / 16;

		  note = 48 + octave * 12 + note;

		  sample = p[1] / 8;
		  vol = (p[1] & 7) + (p[2] / 2);
		  effect = p[2] & 0xF;
		  params = p[3];
		}
	      else
		{
		  note = 0;
		  octave = 0;

		  sample = 0;
		  vol = 0;
		  effect = CMD_NONOTE;
		  params = 0;
		}

	      (*pattern_table[position])[channel][pat].note = note;
	      (*pattern_table[position])[channel][pat].sample = sample;
	      (*pattern_table[position])[channel][pat].command = effect;
	      (*pattern_table[position])[channel][pat].parm1 = params;
	      (*pattern_table[position])[channel][pat].parm2 = 0;
	      (*pattern_table[position])[channel][pat].vol = vol;
	    }

	}

    }

  close (mod_fd);
  return 1;
}

int
load_module (char *name)
{

  struct sample_header
    {
      char            name[22];
      unsigned short  length;	/* In words */

      unsigned char   finetune;
      unsigned char   volume;

      unsigned short  repeat_point;	/* In words */
      unsigned short  repeat_length;	/* In words */
    };

  int             i, mod_fd, total_mem;
  int             sample_ptr, pattern_loc;

  int             position;

  unsigned char  *tune_ptr;	/* array 0-127 */

  char            header[1084];

  int             nr_samples;	/* 16 or 32 samples */
  int             slen, npat;
  char            mname[23];

  ioctl (seqfd, SNDCTL_SEQ_SYNC, 0);
  ioctl (seqfd, SNDCTL_SEQ_RESETSAMPLES, &gus_dev);

  clock_rate = 50.0;

  for (i = 0; i < MAX_POSITION; i++)
    pattern_len[i] = 64;

  for (i = 0; i < MAX_POSITION; i++)
    pattern_tempo[i] = 0;

  if ((mod_fd = open (name, O_RDONLY, 0)) == -1)
    {
      perror (name);
      return 0;
    }

  if (read (mod_fd, header, sizeof (header)) != sizeof (header))
    {
      fprintf (stderr, "%s: Short file (header)\n", name);
      close (mod_fd);
      return 0;
    }

  if (lseek (mod_fd, 0, 0) == -1)
    {
      perror (name);
      close (mod_fd);
      return 0;
    }

  if (header[28] == 0x1a)
    return load_stm_module (mod_fd, name);

  if (*(unsigned short *) &header[0] == 0x6669)
    return load_669_module (mod_fd, name);

  if (!strncmp (header, "MMD0", 4))
    return load_mmd0_module (mod_fd, name);

  fprintf (stderr, "Loading .MOD module: %s\n", name);

  strncpy (mname, header, 22);
  fprintf (stderr, "\nModule: %s - ", mname);

  if (!strncmp (&header[1080], "M.K.", 4) || !strncmp (&header[1080], "FLT8", 4))
    {
      fprintf (stderr, "31 samples\n");
      nr_samples = 31;
    }
  else
    {
      fprintf (stderr, "15 samples\n");
      nr_samples = 15;
    }

  if (nr_samples == 31)
    {
      sample_ptr = pattern_loc = 1084;
      slen = header[950];
      tune_ptr = (unsigned char *) &header[952];
    }
  else
    {
      sample_ptr = pattern_loc = 600;
      slen = header[470];
      tune_ptr = (unsigned char *) &header[472];
    }

  npat = 0;
  for (i = 0; i < 128; i++)
    {
      tune[i] = tune_ptr[i];

      if (tune_ptr[i] > npat)
	npat = tune_ptr[i];
    }
  npat++;

  fprintf (stderr, "Song lenght %d, %d patterns.\n", slen, npat);

  sample_ptr += (npat * 1024);	/* Location where the first sample is stored */
  total_mem = 0;

  for (i = 0; i < 32; i++)
    sample_ok[i] = 0;

  for (i = 0; i < nr_samples; i++)
    {
      int             len, loop_start, loop_end;
      unsigned short  loop_flags = 0;
      char            pname[22];

      struct sample_header *sample;

      struct patch_info *patch;

      sample = (struct sample_header *) &header[20 + (i * 30)];

      len = intelize (sample->length) * 2;
      loop_start = intelize (sample->repeat_point) * 2;
      loop_end = loop_start + (intelize (sample->repeat_length) * 2);

      if (loop_start > len)
	loop_start = 0;
      if (loop_end > len)
	loop_end = len;

      if (loop_end <= loop_start)
	loop_end = loop_start + 1;

      if (loop_end > 2 && loop_end > loop_start)
         loop_flags = WAVE_LOOPING;

      strncpy (pname, sample->name, 20);

      if (len > 0)
	{
	  fprintf (stderr, "Sample %02d: L%05d, S%05d, E%05d V%02d %s\n",
		   i,
		   len,
		   loop_start,
		   loop_end,
		   sample->volume,
		   pname);

	  total_mem += len;

	  patch = (struct patch_info *) malloc (sizeof (*patch) + len);

	  patch->key = GUS_PATCH;
	  patch->device_no = gus_dev;
	  patch->instr_no = i;
	  patch->mode = loop_flags;
	  patch->len = len;
	  patch->loop_start = loop_start;
	  patch->loop_end = loop_end;
	  patch->base_note = 261630;	/* Middle C */
	  patch->base_freq = 8448;
	  patch->low_note = 0;
	  patch->high_note = 20000000;
	  patch->volume = 120;
	  patch->panning = 0;

	  if (lseek (mod_fd, sample_ptr, 0) == -1)
	    {
	      perror (name);
	      close (mod_fd);
	      free (patch);
	      return 0;
	    }

	  sample_ptr += len;

	  if (read (mod_fd, patch->data, len) != len)
	    {
	      fprintf (stderr, "Short file (sample) %d\n", sample_ptr);
	      close (mod_fd);
	      free (patch);
	      return 0;
	    }

	  SEQ_WRPATCH (patch, sizeof (*patch) + len);

	  sample_ok[i] = 1;
	  if (sample->volume == 0) sample->volume = 64;
	  sample_vol[i] = sample->volume;

	  free (patch);
	}
    }

  nr_patterns = npat;
  songlength = slen;
  nr_channels = 4;

  for (position = 0; position < npat; position++)
    {
      unsigned char   patterns[64][4][4];
      int             pat, channel;

      int             pp = pattern_loc + (position * 1024);

      if (lseek (mod_fd, pp, 0) == -1)
	{
	  perror (name);
	  close (mod_fd);
	  return 0;
	}

      if (read (mod_fd, patterns, 1024) != 1024)
	{
	  fprintf (stderr, "Short file (pattern %d) %d\n", tune[position], pp);
	  close (mod_fd);
	  return 0;
	}

      if ((pattern_table[position] = (pattern *) malloc (sizeof (struct note_info) * 64 * nr_channels)) == NULL)
	{
	  fprintf (stderr, "Can't allocate memory for a pattern\n");
	  return 0;
	}

      for (pat = 0; pat < 64; pat++)
	{
	  for (channel = 0; channel < 4; channel++)
	    {
	      unsigned short  tmp;
	      unsigned char  *p;

	      unsigned        period, sample, effect, params, note, vol;

	      p = &patterns[pat][channel][0];

	      tmp = (p[0] << 8) | p[1];
	      sample = (tmp >> 8) & 0x10;
	      period =
		MIN (tmp & 0xFFF, 1023);
	      tmp = (p[2] << 8) | p[3];
	      sample |= tmp >> 12;
	      effect = (tmp >> 8) & 0xF;
	      params = tmp & 0xFF;

	      note = 0;

	      if (period)
		{
		  /*
		   * Convert period to a Midi note number
		   */

		  for (note = 0; note < 37 && period != period_table[note]; note++);
		  if (note >= 37)
		    note = 0;

		  note += 48;
		}

	      vol = 64;

	      if (sample)
		if (effect == 0xc)
		  {
		    vol = params;
		  }
		else
		  vol = sample_vol[sample - 1];

	      vol *= 2;
	      if (vol>64)vol--;

	      (*pattern_table[position])[channel][pat].note = note;
	      (*pattern_table[position])[channel][pat].sample = sample;
	      (*pattern_table[position])[channel][pat].command = effect;
	      (*pattern_table[position])[channel][pat].parm1 = params;
	      (*pattern_table[position])[channel][pat].parm2 = 0;
	      (*pattern_table[position])[channel][pat].vol = vol;
	    }
	}
    }

  close (mod_fd);
  return 1;
}

int
panning (int ch)
{
  static int      panning_tab[] =
  {-110, 110, 110, -110};

  return panning_tab[ch % 4];
}

void
set_speed (int parm)
{
  if (!parm)
    parm = 1;

  if (parm < 32)
    {
      ticks_per_division = parm;
    }
  else
    {
      tick_duration = (60.0 / parm) * 10.0;
    }

}

void
play_module (char *name)
{
  int             i, position, jump_to_pos;

  init_voices ();

  SEQ_START_TIMER ();
#if 1
  for (i=0;i<32;i++)
  {
  	SEQ_EXPRESSION(gus_dev, i, 127);
  	SEQ_MAIN_VOLUME(gus_dev, i, 100);
  }
#endif
  next_time = 0.0;

  set_speed (6);

  for (position = 0; position < songlength; position++)
    {
      int             tick, pattern, channel, pos, go_to;

      pos = tune[position];
      if (pattern_tempo[position])
	set_speed (pattern_tempo[position]);

      jump_to_pos = -1;
      for (pattern = 0; pattern < pattern_len[position] && jump_to_pos == -1; pattern++)
	{
	  this_time = 0.0;

	  for (channel = 0; channel < nr_channels; channel++)
	    {
	      if ((go_to = play_note (channel, &(*pattern_table[pos])[channel][pattern])) != -1)
		jump_to_pos = go_to;

	    }

	  next_time += tick_duration;

	  for (tick = 1; tick < ticks_per_division; tick++)
	    {
	      for (channel = 0; channel < nr_channels; channel++)
		lets_play_voice (channel, &voices[channel]);
	      next_time += tick_duration;
	    }

	}

      if (jump_to_pos >= 0)
	position = jump_to_pos;
    }

  SEQ_WAIT_TIME ((int) next_time + 200);	/* Wait extra 2 secs */

  for (i = 0; i < nr_channels; i++)
    SEQ_STOP_NOTE (gus_dev, i, 0, 127);
  SEQ_DUMPBUF ();

  for (i = 0; i < nr_patterns; i++)
    free (pattern_table[i]);
}

void
sync_time ()
{
  if (next_time > this_time)
    {
      SEQ_WAIT_TIME ((long) next_time);
      this_time = next_time;
    }
}

void
set_volslide (int channel, struct note_info *pat)
{
  int             n;

  voices[channel].volslide = 0;

  if ((n = (pat->parm1 & 0xf0) >> 4))
    voices[channel].volslide = n;
  else
    voices[channel].volslide = pat->parm1 & 0xf;
}

void
set_slideto (int channel, struct note_info *pat)
{
  int             size, rate, dir, range = 200;

  rate = pat->parm1;
  size = voices[channel].note - pat->note;
  if (!size)
    return;

  if (size < 0)
    {
      size *= -1;
      dir = -1;
    }
  else
    dir = 1;

  if (size > 2)
    {
      range = size * 100;
      rate = rate * size / 200;
    }

  rate = pat->parm1 * dir / 30;
  if (!rate)
    rate = 1;

  voices[channel].slide_pitch = 1;
  voices[channel].slide_goal = (dir * 8192 * 200 * 2 / size) / range;
  voices[channel].pitchbender = 0;
  voices[channel].slide_rate = rate;
  SEQ_BENDER_RANGE (gus_dev, channel, range);
}

int
play_note (int channel, struct note_info *pat)
{
  int             jump = -1;
  int             sample;

  if (pat->sample == 0x3f)
    pat->sample = 0;

  if (pat->command == CMD_NONOTE)
    return -1;			/* Undefined */

  sample = pat->sample;

  if (sample && !pat->note)
    {
      pat->note = voices[channel].note;
    }

  if (sample)
    voices[channel].sample = sample;
  else
    sample = voices[channel].sample;

  sample--;

  if (pat->note && pat->command != 3)	/* Have a note -> play */
    {
      if (sample < 0)
	sample = voices[channel].sample - 1;

      if (!sample_ok[sample])
	sample = voices[channel].sample - 1;

      if (sample < 0)
	sample = 0;

      if (sample_ok[sample])
	{
	  sync_time ();

          if (pat->vol > 127) pat->vol=127;
	  SEQ_SET_PATCH (gus_dev, channel, sample);
	  SEQ_PANNING (gus_dev, channel, panning (channel));
	  SEQ_PITCHBEND (gus_dev, channel, 0);
	  SEQ_START_NOTE (gus_dev, channel, pat->note, pat->vol);

	  voices[channel].volume = pat->vol;
	  voices[channel].note = pat->note;
	  voices[channel].slide_pitch = 0;
	}
      else
	SEQ_STOP_NOTE (gus_dev, channel, pat->note, pat->vol);
    }

  switch (pat->command)
    {

    case CMD_NOP:;
      break;

    case CMD_JUMP:
      jump = pat->parm1;
      break;

    case CMD_BREAK:
      jump = -2;
      break;

    case CMD_SPEED:
      set_speed (pat->parm1);
      break;

    case CMD_SLIDEUP:
      voices[channel].slide_pitch = 1;
      voices[channel].slide_goal = 8191;
      voices[channel].pitchbender = 0;
      voices[channel].slide_rate = pat->parm1 * SLIDE_SIZE;
      SEQ_BENDER_RANGE (gus_dev, channel, 200);
      break;

    case CMD_SLIDEDOWN:
      voices[channel].slide_pitch = 1;
      voices[channel].slide_goal = -8192;
      voices[channel].pitchbender = 0;
      voices[channel].slide_rate = -pat->parm1 * SLIDE_SIZE;
      SEQ_BENDER_RANGE (gus_dev, channel, 200);
      break;

    case CMD_SLIDETO:
      set_slideto (channel, pat);
      break;

    case CMD_VOLUME:
      {
        int vol = pat->parm1*2;
        if (vol>127) vol=127;
      if (pat->note && pat->command != 3)
	break;
      SEQ_START_NOTE (gus_dev, channel, 255, vol);
      }
      break;

    case CMD_ARPEG:
      break;

    case 0x0e:
      /* printf ("Cmd 0xE%02x\n", pat->parm1);	*/
      break;

    case CMD_VOLSLIDE:
      set_slideto (channel, pat);
      break;

    default:
      /* printf ("Command %x %02x\n", pat->command, pat->parm1);	*/
    }

  return jump;
}

void
lets_play_voice (int channel, struct voice_info *v)
{
  if (v->slide_pitch)
    {
      v->pitchbender += v->slide_rate;
      if (v->slide_goal < 0)
	{
	  if (v->pitchbender <= v->slide_goal)
	    {
	      v->pitchbender = v->slide_goal;
	      v->slide_pitch = 0;	/* Stop */
	    }
	}
      else
	{
	  if (v->pitchbender >= v->slide_goal)
	    {
	      v->pitchbender = v->slide_goal;
	      v->slide_pitch = 0;	/* Stop */
	    }
	}

      sync_time ();
      SEQ_PITCHBEND (gus_dev, channel, v->pitchbender);
    }

  if (v->volslide)
    {
      v->volume += v->volslide;
      sync_time ();

      if (v->volume > 127) v->volume = 127;
      SEQ_START_NOTE (gus_dev, channel, 255, v->volume);
    }
}
