#! /usr/local/bin/pvtool

#define EXTRA_PARAMETERS 1
#define USAGE_MESSAGE "\
\tfreqshft <input pv file> <output pv file> <freq ratio>\n\
\n\
This program attempts to pitchshift an fft file. It pays no attention to\n\
formants etc.\n"

#define SIMPLE_ALGORITHM 1	/* Faster */

/* There's a third approach that I've yet to investigate: Change the
   spacing of the peaks but maintain their shape. Currently most peaks
   are two or three bins in width (as I've been looking at them) but
   after shifting look quite different. */

int
pvaction (void)
{
  int i, j, bin1, bin2;
  float freq_shift, bin_to_read, dbins, frac;
#if !SIMPLE_ALGORITHM
  float ffrac;
#endif

  freq_shift = fparameter[0];
  printf ("Shifting by frequency ratio %f.\n", freq_shift);

  dbins = (float) bins;

  for (j = 0; j < bins; j++)
    {
      bin_to_read = ((float) j) / freq_shift;
      bin1 = (int) bin_to_read;
      bin2 = bin1 + 1;
      frac = bin_to_read - (float) bin1;

      for (i = 0; i < OUTPUT_FRAMES; i++)
	{
	  if ((int) (1.5 + bin_to_read) >= bins)
	    {
	      OUTPUT_PHIDOT (i, j) = bin_to_freq (j);
	      OUTPUT_MAG (i, j) = 0;
	    }
	  else
	    {
	      OUTPUT_MAG (i, j) =
		((1 - frac) * INPUT1_MAG (i, bin1)
		 + frac * INPUT1_MAG (i, bin2));

#if SIMPLE_ALGORITHM
	      /* I'm not at all sure about this, but it doesn't sound
                 too bad. Weighting is just on freq position: */
	      OUTPUT_PHIDOT (i, j) = freq_shift *
		((1 - frac) * INPUT1_PHIDOT (i, bin1)
		 + frac * INPUT1_PHIDOT (i, bin2));
#else
	      /* I'm not at all sure about this one either. Weighting
                 is on freq position and relative magnitude. This is
                 more complicated and slower and makes no audible
                 difference. However I'm using a very tired old sound
                 card and I'm not testing very thoroughly. That's not
                 even to say this SHOULD work any better. It quite
                 possibly shouldn't. */
	      ffrac = (INPUT1_MAG (i, bin2) * frac)
		/ ((INPUT1_MAG (i, bin1) * (1 - frac))
		   + (INPUT1_MAG (i, bin2) * frac));
	      OUTPUT_PHIDOT (i, j) = freq_shift *
		((1 - ffrac) * INPUT1_PHIDOT (i, bin1)
		 + ffrac * INPUT1_PHIDOT (i, bin2));
#endif
	    }
	}
    }
  return (0);
}
