/* resample.c 
	vi:ts=3 sw=3:
 */

/* $Id: resample.c,v 4.12 1995/02/14 04:02:28 espie Exp $
 * $Log: resample.c,v $
 * Revision 4.12  1995/02/14  04:02:28  espie
 * Nothing.
 *
 * Revision 4.12  1995/02/14  04:02:28  espie
 * Nothing.
 *
 * Revision 4.11  1995/02/06  14:50:47  espie
 * Changed sample_info.
 *
 * Revision 4.11  1995/02/06  14:50:47  espie
 * Changed sample_info.
 *
 * Revision 4.10  1995/02/01  17:14:54  espie
 * Scaled volume.
 *
 * Revision 4.10  1995/02/01  17:14:54  espie
 * Scaled volume.
 *
 * Revision 4.9  1995/02/01  16:39:04  espie
 * Implemented Left/Right channels, 23 bits output.
 *
 * Revision 4.8  1995/02/01  16:30:36  espie
 * *** empty log message ***
 *
 * Revision 4.7  1995/02/01  15:31:25  espie
 * *** empty log message ***
 *
 * Revision 4.6  1995/01/28  09:23:59  espie
 * *** empty log message ***
 *
 * Revision 4.5  1994/11/15  16:11:01  espie
 * *** empty log message ***
 *
 * Revision 4.5  1994/11/15  16:11:01  espie
 * *** empty log message ***
 *
 * Revision 4.4  1994/08/23  18:19:46  espie
 * New sampling function.
 * Finally corrected irksome probleme with MAX_PITCH.
 * First changes to augment the number of channels.
 * Added one level of abstraction. Look through player.c for the
 * old high-level functions.
 * Optimized output and fixed up some details.
 * Unrolled code for oversample = 1 to be more efficient at
 * higher frequency (since a high frequency is better than
 * a higher oversample).
 *
 * Revision 2.14  1992/11/06  19:31:53  espie
 * Fixed missing parameter type.
 * fix_xxx for better speed.
 * set_volume.
 * Added possibility to get back to MONO for the sgi.
 * Added stereo capabilities to the indigo version.
 * Minor bug: a SAMPLE_FAULT is a minor error,
 * we should first check that there was no other
 * error before setting it.
 * New resample function coming from the player.
 * Added more notes.
 *
 * Revision 2.1  1991/11/17  23:07:58  espie
 * Just computes some frequency-related parameters.
 *
 *
 */

#include <math.h>

#include "defs.h"
#include "song.h"
#include "channel.h"
#include "tags.h"
#include "extern.h"
     
ID("$Id: resample.c,v 4.12 1995/02/14 04:02:28 espie Exp $")

/* DO_NOTHING is also used for the automaton */
#define DO_NOTHING 0
#define PLAY 1
#define REPLAY 2

#define MAX_CHANNELS 8

LOCAL struct audio_channel
   {
   struct sample_info *samp;
   int mode;
   unsigned long pointer;
   unsigned long step;
   int volume;
	int scaled_volume;
   int pitch;
	int side;
   } chan[MAX_CHANNELS];

/* left/right */
#define NUMBER_SIDES 2
LOCAL int total[NUMBER_SIDES];

LOCAL int allocated = 0;

struct audio_channel *new_channel_tag_list(prop)
struct tag *prop;
   {
   struct audio_channel *new;

   new = &chan[allocated++];
   new->mode = DO_NOTHING;
   new->pointer = 0;
   new->step = 0;
   new->pitch = 0;
   new->volume = 0;
	new->scaled_volume = 0;
   new->samp = empty_sample();

	while (prop = get_tag(prop))
		{
		switch(prop->type)
			{
		case AUDIO_SIDE:
			new->side = prop->data.scalar;
			break;
		default:
			break;
			}
		prop++;
		}
	total[new->side]++;
   return new;
   }

void release_audio_channels()
   {
   allocated = 0;
   }

/* Have to get some leeway for vibrato (since we don't bound pitch with
 * vibrato). This is conservative.
 */
#define VIB_MAXDEPTH 150


#define C fix_to_int(ch->pointer)

LOCAL unsigned long step_table[REAL_MAX_PITCH + VIB_MAXDEPTH];  
                  /* holds the increment for finding the next sampled
                   * byte at a given pitch (see resample() ).
                   */

/* creates a table for converting ``amiga'' pitch
 * to a step rate at a given resampling frequency.
 * For accuracy, we don't use floating point, but
 * instead fixed point ( << ACCURACY).
 * IMPORTANT NOTES:
 * - we need to make it fit within 32 bits (long), which
 * must be enough for ACCURACY + log2(max sample length)
 * - for the linear resampling to work correctly, we need 
 * sample size (8 bit) + volume size (6 bit) + ACCURACY to fit within a
 * long. Appropriate steps will have to be taken when we switch to 16 bit
 * samples...
 * - never forget that samples are SIGNED numbers. If you have unsigned 
 * samples, you have to convert them SOMEWHERE.
 */

LOCAL void create_step_table(oversample, output_fr)
int oversample;     /* we sample oversample i for each byte output */
int output_fr;      /* output frequency */
   {
   double note_fr; /* note frequency (in Hz) */
   double step;
   int pitch;      /* amiga pitch */

		/* special case: oversample of 0 means linear resampling */
	if (oversample == 0)
		oversample = 1;
   step_table[0] = 0;
   for (pitch = 1; pitch < REAL_MAX_PITCH + VIB_MAXDEPTH; pitch++)
      {
      note_fr = AMIGA_CLOCKFREQ / pitch;
         /* int_to_fix(1) is the normalizing factor */
      step = note_fr / output_fr * int_to_fix(1) / oversample;
      step_table[pitch] = (long)step;
      }
   }
         
LOCAL void readjust_pitch()
   {
   int i;
   for (i = 0; i < allocated; i++)
      chan[i].step = step_table[chan[i].pitch];
   }

void init_tables(oversample, frequency)
int oversample, frequency;
   {
   create_step_table(oversample, frequency);
   readjust_pitch();
   }


/* The playing mechanism itself.
 * According to the current channel automaton,
 * we resample the instruments in real time to
 * generate output.
 */
void resample(oversample, number)
int oversample;
int number;
   {
   int i;            /* sample counter */
   int channel;      /* channel counter */
   int sampling;     /* oversample counter */
	int step;         /* fractional part for linear resampling */
   long value[NUMBER_SIDES];
                     /* recombinations of the various data */
   struct audio_channel *ch;

      /* safety check: we can't have a segv there, provided
       * chan points to a valid sample.
       * For `empty' samples, what is needed is fix_length = 0
       * and rp_start = NULL
       */

		/* do the resampling, i.e., actually play sounds */
		/* code unfolding for special cases */
	switch(oversample)
		{
	case 0:	/* linear resampling */
      for (i = 0; i < number; i++) 
         {
			value[LEFT_SIDE] = value[RIGHT_SIDE] = 0;
         for (channel = 0; channel < allocated; channel++)
            {
            ch = chan + channel;
            switch(ch->mode)
               {
            case DO_NOTHING:
               break;
            case PLAY:
                  /* Since we now have fix_length, we can
                   * do that check with improved performance
                   */
               if (ch->pointer < ch->samp->fix_length)
                  {
						step = fractional_part(ch->pointer);
						value[ch->side] += 
							( (ch->samp->start[C] * (total_step - step) +
							   ch->samp->start[C+1] * step)
							 * (ch->scaled_volume>>8)
							) >> (ACCURACY-8);
                  ch->pointer += ch->step;
                  break;
                  }
               else
                  {
                  ch->mode = REPLAY;
                  ch->pointer -= ch->samp->fix_length;
                  /* FALLTHRU */
                  }
            case REPLAY:
                     /* is there a replay ? */
               if (!ch->samp->rp_start)
                  {
                  ch->mode = DO_NOTHING;
                  break;
                  }
               while (ch->pointer >= ch->samp->fix_rp_length)
                  ch->pointer -= ch->samp->fix_rp_length;
					step = fractional_part(ch->pointer);
					value[ch->side] += 
					 	( (ch->samp->rp_start[C] * (total_step - step) +
							ch->samp->rp_start[C+1] * step)>>8
						 * (ch->scaled_volume >>8)
						) >> (ACCURACY-8);
               ch->pointer += ch->step;
               break;
               }
            } 
				/* some assembly required... */
         output_samples(value[LEFT_SIDE], value[RIGHT_SIDE]);
         }
		break;

	case 1:		/* no oversampling at all */
      for (i = 0; i < number; i++) 
         {
			value[LEFT_SIDE] = value[RIGHT_SIDE] = 0;
         for (channel = 0; channel < allocated; channel++)
            {
            ch = chan + channel;
            switch(ch->mode)
               {
            case DO_NOTHING:
               break;
            case PLAY:
                  /* Since we now have fix_length, we can
                   * do that check with improved performance
                   */
               if (ch->pointer < ch->samp->fix_length)
                  {
                  value[ch->side] += ch->samp->start[C] * ch->scaled_volume; 
                  ch->pointer += ch->step;
                  break;
                  }
               else
                  {
                  ch->mode = REPLAY;
                  ch->pointer -= ch->samp->fix_length;
                  /* FALLTHRU */
                  }
            case REPLAY:
                     /* is there a replay ? */
               if (!ch->samp->rp_start)
                  {
                  ch->mode = DO_NOTHING;
                  break;
                  }
               while (ch->pointer >= ch->samp->fix_rp_length)
                  ch->pointer -= ch->samp->fix_rp_length;
               value[ch->side] += ch->samp->rp_start[C] * ch->scaled_volume; 
               ch->pointer += ch->step;
               break;
					}
            } 
         output_samples(value[LEFT_SIDE], value[RIGHT_SIDE]);
         }
		break;
	default:		/* standard oversampling code */
      for (i = 0; i < number; i++) 
         {
			value[LEFT_SIDE] = value[RIGHT_SIDE] = 0;
         for (channel = 0; channel < allocated; channel++)
            {
            for (sampling = 0; sampling < oversample; sampling++)
               {
               ch = chan + channel;
               switch(ch->mode)
                  {
               case DO_NOTHING:
                  break;
               case PLAY:
                     /* Since we now have fix_length, we can
                      * do that check with improved performance
                      */
                  if (ch->pointer < ch->samp->fix_length)
                     {
                     value[ch->side] += ch->samp->start[C] * ch->scaled_volume;
                     ch->pointer += ch->step;
                     break;
                     }
                  else
                     {
                     ch->mode = REPLAY;
                     ch->pointer -= ch->samp->fix_length;
                     /* FALLTHRU */
                     }
               case REPLAY:
                        /* is there a replay ? */
                  if (!ch->samp->rp_start)
                     {
                     ch->mode = DO_NOTHING;
                     break;
                     }
                  while (ch->pointer >= ch->samp->fix_rp_length)
                     ch->pointer -= ch->samp->fix_rp_length;
                  value[ch->side] += ch->samp->rp_start[C] * ch->scaled_volume;
                  ch->pointer += ch->step;
                  break;
                  }
               } 
            }
         output_samples(value[LEFT_SIDE]/oversample, 
            value[RIGHT_SIDE]/oversample);
         }   
      }

#ifndef __NeXT__
      /* On the NeXT you may not flush your buffer that often, because the
       * collected data is not enough for continous play.
       */
   flush_buffer();
#endif
   }


/* setting up a given note */

void play_note(au, samp, pitch)
struct audio_channel *au;
struct sample_info *samp;
int pitch;
   {
   au->pointer = 0;
   au->pitch = pitch;
   au->step = step_table[pitch];
	au->samp = samp;
	au->scaled_volume = au->samp->volume_lookup[au->volume];
	au->mode = PLAY;
   }

/* changing the current pitch (value
 * may be temporary, and not stored
 * in channel pitch, for instance vibratos.
 */
void set_play_pitch(au, pitch)
struct audio_channel *au;
int pitch;
   {
      /* save current pitch in case we want to change
       * the step table on the run
       */
   au->pitch = pitch;
   au->step = step_table[pitch];
   }

/* changing the current volume. You HAVE to get through
 * there so that it will work on EVERY machine.
 */
void set_play_volume(au, volume)
struct audio_channel *au;
int volume;
   {
   au->volume = volume;
	au->scaled_volume = au->samp->volume_lookup[volume];
   }

void set_play_position(au, pos)
struct audio_channel *au;
int pos;
   {
   au->pointer = int_to_fix(pos);
   }

