
/*
*	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/soundcard.h>
#include <sys/ultrasound.h>

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

int
play_note (int channel, int position, int pattern, struct note_info *pat)
{
  int jump = 0;
  int sample, note;
  int no_sample = FALSE;
  int old_sample, old_arpeg, old_vibra, old_tremolo;
  int tmp_int;
  int extend_arg;

  voices[channel].slide_pitch = 0;
  voices[channel].volslide = 0;
  voices[channel].retrigger = 0;
  old_arpeg = voices[channel].arpeg_num;
  old_vibra = voices[channel].vibra_rate;
  old_tremolo = voices[channel].tremolo;
  voices[channel].arpeg_num = 0;
  voices[channel].vibra_rate = 0;
  voices[channel].tremolo = 0;

  old_sample = voices[channel].sample - 1;
  note = pat->note;

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

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

  sample = pat->sample;

  /* A sample with no note resets the volume to its default, but should not */
  /* retrigger the note.  This really isn't possible with the current drivers */
  /* if the sample changes, so the note is retriggered on a sample change */

  if (sample && !note)
    {
      if (old_sample != (sample - 1))
	note = voices[channel].note;
      else if (voices[channel].volume != pat->vol)
	{
	  sync_time ();
	  SEQ_START_NOTE (gus_dev, channel, 255, pat->vol);
	  voices[channel].volume = pat->vol;
	}
    }

  if (sample)
    voices[channel].sample = sample;
  else
    {
      no_sample = TRUE;
      sample = voices[channel].sample;
    }

  sample--;

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

      /* if (!sample_ok[sample])
	sample = voices[channel].sample - 2; */

      if (sample < 0)
	sample = 0;

      sync_time ();

      if (sample_ok[sample])
	{
	  if (pat->vol > 127)
	    pat->vol = 127;

	  if (old_sample != sample)
	    {
	      SEQ_SET_PATCH (gus_dev, channel, sample);
	      voices[channel].sample = sample + 1;
	    }

	  if (voices[channel].pitchbender || old_arpeg || old_vibra)
	    {
	      SEQ_PITCHBEND (gus_dev, channel, 0);
	      voices[channel].pitchbender = 0;
	      old_arpeg = 0;
	      old_vibra = 0;
	    }

	  /* specifying no sample keeps the current volume */
	  if ((no_sample == FALSE) || (pat->command == CMD_VOLUME))
	    voices[channel].volume = pat->vol;

	  if ((pat->command != CMD_EXTENDED) ||
	      ((pat->parm1 & 0xf0) != CMD_DELAY_NOTE))
	    SEQ_START_NOTE (gus_dev, channel, note, voices[channel].volume);

	  voices[channel].note = note;
	  voices[channel].slide_period = pat->period;

	  if (voices[channel].vibra_wave <= 3)
	    voices[channel].vibra_position = 0;
	  if (voices[channel].tremolo_wave <= 3)
	    voices[channel].tremolo_position = 0;
	}
      else
	SEQ_START_NOTE (gus_dev, channel, 255, 0);
    }

  switch (pat->command)
    {

    case CMD_NOP:;
      break;

    case CMD_ARPEG:
      if (pat->parm1)
	set_arpeg (channel, pat->parm1);
      break;

    case CMD_JUMP:
      jump = MOVE_JUMP;
      effects.position = pat->parm1;
      effects.pattern = 0;
      break;

    case CMD_BREAK:
      jump = MOVE_JUMP;
      effects.position = position + 1;
      if (effects.position < songlength)
	if (pat->parm1 < pattern_len[tune[effects.position]])
	  effects.pattern = pat->parm1;
	else
	  effects.pattern = 0;
      else
	effects.pattern = 0;
      break;

    case CMD_SPEED:
      if (pat->parm1)
	set_speed (pat->parm1);
      else
	jump = MOVE_EXIT;
      break;

    case CMD_SLIDEUP:
      if (((voices[channel].note * 100 + voices[channel].pitchbender) <= 8400) && (pat->parm1 > 0))
	{
	  tmp_int = pat->note;
	  pat->note = 84;
	  set_slideto (channel, pat);
	  pat->note = tmp_int;
	  voices[channel].slide_pitch = SLIDE_UPDOWN;
	}
      break;

    case CMD_SLIDEDOWN:
      if (((voices[channel].note * 100 + voices[channel].pitchbender) >= 4800) && (pat->parm1 > 0))
	{
	  tmp_int = pat->note;
	  pat->note = 48;
	  set_slideto (channel, pat);
	  pat->note = tmp_int;
	  voices[channel].slide_pitch = SLIDE_UPDOWN;
	}
      break;

    case CMD_SLIDETO:
      set_slideto (channel, pat);
      voices[channel].slide_pitch = SLIDE_PORT;
      break;

    case CMD_SETOFFSET:
      sync_time ();
      /* if there is no note, restart current note to prevent click */
      if (!pat->note)
	SEQ_START_NOTE (gus_dev, channel, voices[channel].note, voices[channel].volume);
      GUS_VOICE_POS (gus_dev, channel, pat->parm1 * 256);
      break;

    case CMD_VOLUME:
      {
	int vol = pat->parm1 * 2;
	if (vol > 127)
	  vol = 127;
	if (voices[channel].volume != vol)
	  {
	    voices[channel].volume = vol;
	    sync_time ();
	    SEQ_START_NOTE (gus_dev, channel, 255, vol);
	  }
      }
      break;

    case CMD_EXTENDED:
      extend_arg = pat->parm1 & 0x0f;
      switch (pat->parm1 & 0xf0)
	{
	case CMD_VIBRA_WAVE:
	  if ((extend_arg & 0x03) == 0x03)
	    extend_arg -= 1;
	  voices[channel].vibra_wave = extend_arg;
	  break;
	case CMD_TREMOLO_WAVE:
	  if ((extend_arg & 0x03) == 0x03)
	    extend_arg -= 1;
	  voices[channel].tremolo_wave = extend_arg;
	  break;
	case CMD_GLISSANDO:
	  voices[channel].glissando = extend_arg;
	  break;
	case CMD_DELAY_PAT:
	  effects.delay_notes = extend_arg;
	  break;
	case CMD_CUT_NOTE:
	  voices[channel].cut_count = extend_arg + 1;
	  break;
	case CMD_DELAY_NOTE:
	  if (extend_arg == 0)
	    extend_arg = 1;
	  voices[channel].delay_count = extend_arg + 1;
	  break;
	case CMD_PATTERN_LOOP:
	  if (extend_arg == 0)
	    voices[channel].pattern = pattern;
	  else
	    {
	      effects.loop_chan = channel;
	      if (voices[channel].loop_times == 0)
		{
		  voices[channel].loop_times = extend_arg;
		  effects.pattern = voices[channel].pattern;
		  jump = MOVE_LOOP;
		}
	      else if (--voices[channel].loop_times > 0)
		{
		  effects.pattern = voices[channel].pattern;
		  jump = MOVE_LOOP;
		}
	    }
	  break;
	case CMD_RETRIGGER:
	  voices[channel].retrigger = extend_arg;
	  if (note)
	    voices[channel].retrig_count = extend_arg + 1;
	  else
	    voices[channel].retrig_count = 1;
	  break;
	case CMD_FINEVOLUP:
	  voices[channel].finevol = TRUE;
	  voices[channel].volslide = extend_arg * 2;
	  break;
	case CMD_FINEVOLDOWN:
	  voices[channel].finevol = TRUE;
	  voices[channel].volslide = -extend_arg * 2;
	  break;
	case CMD_FINEPORTUP:
	  if (extend_arg > 0)
	    {
	      voices[channel].slide_pitch = SLIDE_ONCE;
	      voices[channel].slide_rate = extend_arg;
	    }
	  break;
	case CMD_FINEPORTDOWN:
	  if (extend_arg > 0)
	    {
	      voices[channel].slide_pitch = SLIDE_ONCE;
	      voices[channel].slide_rate = -extend_arg;
	    }
	  break;
	}
      break;

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

    case CMD_PORTANDVOL:
      set_volslide (channel, pat);
      voices[channel].slide_pitch = 1;
      break;

    case CMD_VIBRATO:
      set_vibrato (channel, pat->parm1);
      break;

    case CMD_VIBRAANDVOL:
      set_vibrato (channel, 0);
      set_volslide (channel, pat);
      break;

    case CMD_TREMOLO:
      set_tremolo (channel, pat->parm1);
      break;

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

  if (((old_arpeg && !voices[channel].arpeg_num) ||
       (old_vibra && !voices[channel].vibra_rate)) &&
      !pat->note)
    {
      sync_time ();
      SEQ_PITCHBEND (gus_dev, channel, voices[channel].pitchbender + 1);
    }

  if (old_tremolo && !voices[channel].tremolo &&
      (!pat->note || pat->command == CMD_SLIDETO))
    {
      sync_time ();
      SEQ_START_NOTE (gus_dev, channel, 255, voices[channel].volume);
    }

  return jump;
}
