
/*
*	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 "defines.h"
#include "structs.h"
#include "globals.h"
#include "protos.h"

void
lets_play_voice (int channel, struct voice_info *v,
		 struct song_info *song_char, int tick_no)
{
  int note, bend, vibra;

  if ((v->slide_pitch && v->slide_rate) &&
      ((tick_no > 0) || (v->slide_pitch == SLIDE_ONCE)))
    {
      if (song_char->slide_type == SLIDE_PERIOD_LIN)
	v->slide_period -= v->slide_rate;
      else
	v->slide_period = 111978 / (111978 / v->slide_period + v->slide_rate);

      period_to_note (v->slide_period, &note, &bend);
      if ((v->slide_pitch == SLIDE_PORT) && v->glissando)
	{
	  if (bend > 0)
	    note += 1;
	  bend = 0;
	}
      v->pitchbender = (note * 100 + bend) - (v->note * 100);
      if (v->slide_pitch == SLIDE_ONCE)
	{			/* slide one step only */
	  v->slide_pitch = 0;
	  v->slide_rate = 0;
	}
      else if (v->slide_rate < 0)
	{
	  if (v->pitchbender <= v->slide_goal)
	    {
	      v->pitchbender = v->slide_goal;
	      v->slide_period = v->slide_period_goal;
	      if (v->slide_pitch == SLIDE_PORT)
		v->last_note = 0;
	      v->slide_pitch = 0;	/* Stop */
	      v->slide_rate = 0;
	      v->fineslide_adj = 0;
	    }
	}
      else
	{
	  if (v->pitchbender >= v->slide_goal)
	    {
	      v->pitchbender = v->slide_goal;
	      v->slide_period = v->slide_period_goal;
	      if (v->slide_pitch == SLIDE_PORT)
		v->last_note = 0;
	      v->slide_pitch = 0;	/* Stop */
	      v->slide_rate = 0;
	      v->fineslide_adj = 0;
	    }
	}

      /* correct for rounding by the driver */
      vibra = v->pitchbender + v->finetune;
      if (vibra >= 0)
	vibra++;
      else
	vibra--;

      sync_time ();
      SEQ_PITCHBEND (gus_dev, channel, vibra);
    }

  if ((v->volslide) && ((tick_no > 0) || (v->finevol == TRUE)))
    {
      vibra = v->volume + v->volslide;

      if (vibra > 255)
	{
	  vibra = 255;
	  v->volslide = 0;
	}
      else if (vibra < 0)
	{
	  vibra = 0;
	  v->volslide = 0;
	}

      if (v->finevol == TRUE)
	{
	  v->volslide = 0;
	  v->finevol = FALSE;
	}

      v->volume = (unsigned char) vibra;

      if (song_char->vol_type == VOL_LOG)
	vibra = vol_log_to_lin ((unsigned char) vibra) / 2;
      else
	vibra /= 2;

      sync_time ();
      SEQ_START_NOTE (gus_dev, channel, 255, (unsigned char) vibra);
    }

  if (v->cut_count)
    if (v->cut_count == tick_no)
      {
	sync_time ();
	SEQ_START_NOTE (gus_dev, channel, 255, 0);
	voices[channel].volume = 0;
      }

  if (v->delay_count)
    if (v->delay_count == tick_no)
      {
	vibra = v->volume;
	if (song_char->vol_type == VOL_LOG)
	  vibra = vol_log_to_lin ((unsigned char) vibra) / 2;
	else
	  vibra /= 2;

	bend = v->finetune;
	if (bend >= 0)
	  bend++;
	else
	  bend--;

	sync_time ();
	SEQ_START_NOTE (gus_dev, channel, v->note, vibra);
	SEQ_PITCHBEND (gus_dev, channel, bend);
      }

  if ((v->retrigger) && (tick_no > 0))
    if ((tick_no % v->retrigger) == 0)
      {
	vibra = v->volume;
	if ((v->retrig_vol != 0) && (v->retrig_vol != 8))
	  {
	    switch (v->retrig_vol)
	      {
	      case 6:
		vibra = (vibra * 2) / 3;
		break;
	      case 7:
		vibra /= 2;
		break;
	      case 14:
		vibra = (vibra * 3) / 2;
		break;
	      case 15:
		vibra *= 2;
		break;
	      default:
		if (v->retrig_vol > 8)
		  vibra += ((1 << (v->retrig_vol - 9)) * VOL_SLIDE_RATE);
		else
		  vibra -= ((1 << (v->retrig_vol - 1)) * VOL_SLIDE_RATE);
		break;
	      }
	    if (vibra > 255)
	      vibra = 255;
	    else if (vibra < 0)
	      vibra = 0;

	    v->volume = (unsigned char) vibra;
	  }

	if (song_char->vol_type == VOL_LOG)
	  vibra = vol_log_to_lin ((unsigned char) vibra) / 2;
	else
	  vibra /= 2;

	sync_time ();
	SEQ_START_NOTE (gus_dev, channel, v->note, vibra);
      }

  if (v->arpeg_num)
    {
      v->arpeg_curr = tick_no % v->arpeg_num;

      /* correct for rounding by the driver */
      vibra = v->pitchbender + v->arpeg_note[v->arpeg_curr] + v->finetune;
      if (vibra >= 0)
	vibra++;
      else
	vibra--;

      sync_time ();
      SEQ_PITCHBEND (gus_dev, channel, vibra);
    }

  if ((v->vibra_rate) && (tick_no > 0))
    {
      vibra = vibra_table[v->vibra_wave & 0x03][v->vibra_position];
      vibra = (vibra * v->vibra_depth) / 128;
      period_to_note (v->slide_period + vibra, &note, &bend);
      vibra = (note * 100 + bend) - (v->note * 100) + v->finetune;

      /* correct for rounding by the driver */
      if (vibra >= 0)
	vibra++;
      else
	vibra--;

      sync_time ();
      SEQ_PITCHBEND (gus_dev, channel, vibra);
      v->vibra_position += v->vibra_rate;
      v->vibra_position %= NUM_VIBRA;
    }

  if ((v->tremolo) && (tick_no > 0))
    {
      vibra = vibra_table[v->tremolo_wave & 0x03][v->tremolo_position];
      vibra = (vibra * v->tremolo_depth) / 128;
      vibra = v->volume + (VOL_SLIDE_RATE * vibra);
      if (vibra > 255)
	vibra = 255;
      else if (vibra < 0)
	vibra = 0;

      if (song_char->vol_type == VOL_LOG)
	vibra = vol_log_to_lin ((unsigned char) vibra) / 2;
      else
	vibra /= 2;

      sync_time ();
      SEQ_START_NOTE (gus_dev, channel, 255, (unsigned char) vibra);
      v->tremolo_position += v->tremolo;
      v->tremolo_position %= NUM_VIBRA;
    }

  if (v->tremor)
    {
      note = tick_no % v->trem_total;
      if (note == v->tremor)
	{			/* time to turn note off */
	  sync_time ();
	  SEQ_START_NOTE (gus_dev, channel, 255, 0);
	  v->volume = 0;
	}
      else if (note == 0)
	{			/* time to turn note on */
	  vibra = v->trem_volume;
	  v->volume = vibra;
	  if (song_char->vol_type == VOL_LOG)
	    vibra = vol_log_to_lin ((unsigned char) vibra) / 2;
	  else
	    vibra /= 2;
	  sync_time ();
	  SEQ_START_NOTE (gus_dev, channel, 255, (unsigned char) vibra);
	}
    }
}
