
/*
*	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 <stdio.h>
#include <stdlib.h>
#include <strings.h>

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

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];
  int bend;

  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) ||
      !strncmp (&header[1080], "FLT4", 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 length %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[23];		/* changed from [22] by AJR */

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

      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 > 1 && loop_end > loop_start)
	loop_flags = WAVE_LOOPING;

      strncpy (pname, sample->name, 22);
      pname[22] = '\0';

      if (len > 0)
	{
	  fprintf (stderr, "Sample %02d: L%06d, S%06d, E%06d 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 = base_freq_table[sample->finetune & 0xf];
	  patch->low_note = 0;
	  patch->high_note = 20000000;
	  patch->volume = 0 /* changed from 120 by AJR */ ;
	  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;
	    }

	  /* try to remove loop clicking */

	  if ((loop_flags & WAVE_LOOPING) && (loop_end >= 2))
	    {
	      patch->data[loop_end] = patch->data[loop_start];
	      patch->data[loop_end - 1] =
		((signed char) (patch->data[loop_end - 2]) +
		 (signed char) (patch->data[loop_end])) / 2;
	    }

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

		  period_to_note (period, &note, &bend);
		}

	      vol = 64;

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

	      vol *= 2;
	      if (vol > 127)
		vol = 127;

	      (*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;
	      (*pattern_table[position])[channel][pat].period = period;
	    }
	}
    }

  close (mod_fd);
  return 1;
}
