/**********************************************************************
audio sample rate converter

$Id: rateconv.c,v 1.10 2003/10/12 00:21:46 purnhage Exp $

Source file: rateconv.c

(c) Heiko Purnhagen 1998-2003
e-mail: purnhage@tnt.uni-hannover.de
WWW:    http://www.tnt.uni-hannover.de/~purnhage/

This module is available under the terms of the GNU Lesser General
Public License. See file COPYING-LGPL for further details. All usual
disclaimers apply!

Authors:
NM    Nikolaus Meine, Uni Hannover (c/o Heiko Purnhagen)
HP    Heiko Purnhagen, Uni Hannover <purnhage@tnt.uni-hannover.de>

Changes:
xx-jun-98   NM      resamp.c
18-sep-98   HP      converted into module
03-aug-99   NM      now even faster ...
04-nov-99   NM/HP   double ratio, htaps1 /= 2
11-nov-99   HP      improved RateConvInit() debuglevel
18-Aug-2001 HP      added fracOff
14-Nov-2001 HP      more debug output ...
2003-03-15  HP      added multichannel support ...
2003-10-11  HP      ... now for real ;-)
**********************************************************************/

/*
 * Sample-rate converter
 *
 * Realized in three steps:
 *
 * 1. Upsampling by factor two while doing appropriate lowpass-filtering.
 *    This is done by using an FFT-based convolution algorithm with a multi-tap
 *    Kaiser-windowed lowpass filter.
 *    If the cotoff-frequency is less than 0.5, only lowpass-filtering without
 *    the upsampling is done.
 * 2. Upsampling by factor 128 using a 15 tap Kaiser-windowed lowpass filter
 *    (alpha=12.5) and conventional convolution algorithm.
 *    Two values (the next neighbours) are computed for every sample needed.
 * 3. Linear interpolation between the two bounding values.
 *
 * Stereo and mono data is supported.
 * Up- and downsampling of any ratio is possible.
 * Input and output file format is Sun-audio.
 *
 * Written by N.Meine, 1998
 *
 */

/* Multi channel data is interleaved: l0 r0 l1 r1 ... */
/* Total number of samples (over all channels) is used. */


#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "rateconv.h"
#include "common.h"


/* ---------- declarations ---------- */

#ifndef MSVC
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
#endif

/* ---------- declarations (structures) ---------- */

#define real	double	/* float seems to be precise enough, but it	*/
			/* doesn't run much faster than double		*/

typedef struct {
	real	re,im;
} complex;


struct RCBufStruct		/* buffer handle */
{
  int numChannel;		/* number of channels */
  long currentSampleIn;		/* number of input samples */
  long currentSampleOut;	/* number of output samples */

  real *wtab;
  complex *x;
  real *tp1;
  real *tp2;
  complex *ibuf;

  long nfn;
  long adv;
  long istart;
  long numode;
  long fm;
  double p1;
  double d1;

  long outSize;
  float *out;
};


/* ---------- variables ---------- */

static int RCdebugLevel = 0;	/* debug level */


#define deftaps1	485

#define ov2exp	7		/* Oversampling exponent in the second step */
#define ov2fac	(1<<ov2exp)	/* Oversampling factor in the second step */
#define htaps2	7		/* Num. taps of the 2nd filter: 2*htaps2+1 */
#define alpha2	12.5		/* Kaiser-parameter for the second filter */
#define cutoff2	0.97		/* cutoff-frequency for the second filter */

#define cexp	(CACHE1-(sizeof(real)==4 ? 3 : 4 ))
#define cache	(1<<cexp)

#define pi	3.1415926535897932
#define zpi	6.2831853071795965


/* ---------- local functions ---------- */


static void mkeiwutab(real *st,long fn)
{
	register int	i,k;
	register double	phi;

	for (k=fn; k; k>>=1)
	{
		phi = zpi/((double) k);
		for (i=0; i<k; i++) st[i]=(real) sin(phi*((double) i));
		st+=k;
	}
}

static void r4fftstep(register real *x0,register long n,register real *t)
{
	register long		n4 = n/4;
	register real		*x1 = x0+2*n4;
	register real		*x2 = x1+2*n4;
	register real		*x3 = x2+2*n4;
	register real		*y;
	register real		wr,wi,ar,ai,br,bi,cr,ci,dr,di,hr,hi;
	register long		i;


	if ( n>16 )
	{
		y=t+6*n4;
		r4fftstep(x0,n4,y);
		r4fftstep(x1,n4,y);
		r4fftstep(x2,n4,y);
		r4fftstep(x3,n4,y);
	}
	else
	{
		if ( n==8 )
		{
			for (y=x0; y<x0+16; y+=4)
			{
				ar=y[0]; ai=y[1];
				br=y[2]; bi=y[3];
				y[0]=ar+br;
				y[1]=ai+bi;
				y[2]=ar-br;
				y[3]=ai-bi;
			}
		}
		else
		{
			for (y=x0; y<x0+32; y+=8)
			{
				ar=y[0]; ai=y[1];
				br=y[2]; bi=y[3];
				cr=y[4]; ci=y[5];
				dr=y[6]; di=y[7];
				hr=ar+cr; wr=ar-cr; hi=ai+ci; wi=ai-ci;
				ar=br+dr; cr=br-dr; ai=bi+di; ci=bi-di;
				y[0]=hr+ar; y[1]=hi+ai;
				y[2]=wr+ci; y[3]=wi-cr;
				y[4]=hr-ar; y[5]=hi-ai;
				y[6]=wr-ci; y[7]=wi+cr;
			}
		}
	}

	y=t+n4;
	for (i=0; i<n4; i++)
	{
		ar=x0[0]; ai=x0[1];
		wr=y[  i]; wi=t[  i]; hr=x1[0]; hi=x1[1]; br=wr*hr+wi*hi; bi=wr*hi-wi*hr;
		wr=y[2*i]; wi=t[2*i]; hr=x2[0]; hi=x2[1]; cr=wr*hr+wi*hi; ci=wr*hi-wi*hr;
		wr=y[3*i]; wi=t[3*i]; hr=x3[0]; hi=x3[1]; dr=wr*hr+wi*hi; di=wr*hi-wi*hr;
		hr=ar+cr; wr=ar-cr; hi=ai+ci; wi=ai-ci;
		ar=br+dr; cr=br-dr; ai=bi+di; ci=bi-di;
		x0[0]=hr+ar; x0[1]=hi+ai;
		x1[0]=wr+ci; x1[1]=wi-cr;
		x2[0]=hr-ar; x2[1]=hi-ai;
		x3[0]=wr-ci; x3[1]=wi+cr;
		x0+=2; x1+=2; x2+=2; x3+=2;
	}
}


static void r4ifftstep(register real *x0,register long n,register real *t)
{
	register long		n4 = n/4;
	register real		*x1 = x0+2*n4;
	register real		*x2 = x1+2*n4;
	register real		*x3 = x2+2*n4;
	register real		*y;
	register real		wr,wi,ar,ai,br,bi,cr,ci,dr,di,hr,hi;
	register long		i;

	if ( n>16 )
	{
		y=t+6*n4;
		r4ifftstep(x0,n4,y);
		r4ifftstep(x1,n4,y);
		r4ifftstep(x2,n4,y);
		r4ifftstep(x3,n4,y);
	}
	else
	{
		if ( n==8 )
		{
			for (y=x0; y<x0+16; y+=4)
			{
				ar=y[0]; ai=y[1];
				br=y[2]; bi=y[3];
				y[0]=ar+br;
				y[1]=ai+bi;
				y[2]=ar-br;
				y[3]=ai-bi;
			}
		}
		else
		{
			for (y=x0; y<x0+32; y+=8)
			{
				ar=y[0]; ai=y[1];
				br=y[2]; bi=y[3];
				cr=y[4]; ci=y[5];
				dr=y[6]; di=y[7];
				hr=ar+cr; wr=ar-cr; hi=ai+ci; wi=ai-ci;
				ar=br+dr; cr=br-dr; ai=bi+di; ci=bi-di;
				y[0]=hr+ar; y[1]=hi+ai;
				y[2]=wr-ci; y[3]=wi+cr;
				y[4]=hr-ar; y[5]=hi-ai;
				y[6]=wr+ci; y[7]=wi-cr;
			}
		}
	}

	y=t+n4;
	for (i=0; i<n4; i++)
	{
		ar=x0[0]; ai=x0[1];
		wr=y[  i]; wi=t[  i]; hr=x1[0]; hi=x1[1]; br=wr*hr-wi*hi; bi=wr*hi+wi*hr;
		wr=y[2*i]; wi=t[2*i]; hr=x2[0]; hi=x2[1]; cr=wr*hr-wi*hi; ci=wr*hi+wi*hr;
		wr=y[3*i]; wi=t[3*i]; hr=x3[0]; hi=x3[1]; dr=wr*hr-wi*hi; di=wr*hi+wi*hr;
		hr=ar+cr; wr=ar-cr; hi=ai+ci; wi=ai-ci;
		ar=br+dr; cr=br-dr; ai=bi+di; ci=bi-di;
		x0[0]=hr+ar; x0[1]=hi+ai;
		x1[0]=wr-ci; x1[1]=wi+cr;
		x2[0]=hr-ar; x2[1]=hi-ai;
		x3[0]=wr+ci; x3[1]=wi-cr;
		x0+=2; x1+=2; x2+=2; x3+=2;
	}
}


static void perm4(real *x,long p)
{
	register real	a,b,c,d;
	register long	i,j,k,l;

	l=1<<(p-1);
	for (i=j=0; i<(2<<p); i+=2)
	{
		if ( j>i )
		{
			a=x[i]; b=x[i+1]; c=x[j]; d=x[j+1]; x[j]=a; x[j+1]=b; x[i]=c; x[i+1]=d;	
		}
		j+=l;
		for (k=l<<2; j&k; k>>=2) j=(j^=k)+(k>>4);
	}
}

static void perm42(real *x,long p)
{
	register real	*y,*z;
	register long	i,n;

	n=1<<p;
	perm4(x  ,p-1);
	perm4(x+n,p-1);

	y=x+2*n;
	z=x+n;
	for (i=0; i<n; i+=2)
	{
		y[2*i  ]=x[i  ];
		y[2*i+1]=x[i+1];
		y[2*i+2]=z[i  ];
		y[2*i+3]=z[i+1];
		x[i  ]=y[i  ];
		x[i+1]=y[i+1];
	}
	for (; i<2*n; i+=2)
	{
		x[i  ]=y[i  ];
		x[i+1]=y[i+1];
	}
}

static void r4fft(real *t,real *x,long p)
{
	register long	n  = 1<<p;

	if ( p&1 ) perm42(x,p); else perm4(x,p);
	r4fftstep(x,n,t);
}

static void r4ifft(real *t,real *x,long p)
{
	register long	n  = 1<<p;

	if ( p&1 ) perm42(x,p); else perm4(x,p);
	r4ifftstep(x,n,t);
}


static double bessel(double x)
{
  register double	p,s,ds,k;

  x*=0.5;
  k=0.0;
  p=s=1.0;
  do
    {
      k+=1.0;
      p*=x/k;
      ds=p*p;
      s+=ds;
    }
  while ( ds>1.0e-17*s ); 

  return s;
}

static void mktp1(complex *y,double fg,double a,long fn,long htaps1)
{
  register long	i;
  register double	f,g,x,px;

  y[0].re=fg;
  y[0].im=0.0;

  f=1.0/bessel(a);
  g=1.0/(htaps1+0.5);
  g*=g;

  for (i=1; i<=htaps1; i++)
    {
      x=(double) i;
      px=pi*x;
      y[i].re=(real) f*bessel(a*sqrt(1.0-g*x*x))*sin(fg*px)/px;
      y[i].im=(real) 0.0;
      y[fn-i]=y[i];
    }
  for (; i<fn-htaps1; i++) y[i].re=y[i].im=0.0;
}

static void mktp2(real *y,double fg,double a)
{
  register long	i;
  register double	f,g,x,px;

  y[0]=fg;

  f=1.0/bessel(a);
  g=1.0/(((double) ((htaps2+1)*ov2fac))-0.5);
  g*=g;

  for (i=1; i<(htaps2+1)*ov2fac; i++)
    {
      x=(double) i;
      px=(pi/ov2fac)*x;
      y[i]=(real) f*bessel(a*sqrt(1.0-g*x*x))*sin(fg*px)/px;
    }
  for (; i<(htaps2+2)*ov2fac; i++) y[i]=0.0;
}

static void ctp2_m(real *x,double p,real *k,float **out)
{
  register long	n,i,j;
  register double	h,l,r;
  register real	*ka,*kb;
  register real	*xa,*xb;

  h=ov2fac*p;
  i=(long) h;
  h-=(double) i;

  j=i&(ov2fac-1); i>>=ov2exp;

  l=r=0.0;
  ka=k+j;	kb=k+ov2fac-j;
  i<<=1;
  xa=x+i;	xb=x+i+2;

  n=htaps2+1;
  do
    {
      l+=xa[0]*ka[ 0] + xb[0]*kb[ 0];
      r+=xa[0]*ka[ 1] + xb[0]*kb[-1];

      ka+=ov2fac;
      kb+=ov2fac;
      xa-=2;
      xb+=2;
    }
  while (--n);

  *(*out)++ = (float) (l+h*(r-l));
}

static void ctp2_s(complex *x,double p,real *k,float **out,long stereo,long skip)
{
  register long	n,i,j;
  register double	h,l0,l1,r0,r1;
  register real	*ka,*kb;
  register complex *xa,*xb;

  h=ov2fac*p;
  i=(long) h;
  h-=(double) i;

  j=i&(ov2fac-1); i>>=ov2exp;

  l0=l1=r0=r1=0.0;
  ka=k+j;	kb=k+ov2fac-j;
  xa=x+i;	xb=x+i+1;

  n=htaps2+1;
  do
    {
      l0+=xa[0].re*ka[ 0] + xb[0].re*kb[ 0];
      l1+=xa[0].im*ka[ 0] + xb[0].im*kb[ 0];
      r0+=xa[0].re*ka[ 1] + xb[0].re*kb[-1];
      r1+=xa[0].im*ka[ 1] + xb[0].im*kb[-1];

      ka+=ov2fac;
      kb+=ov2fac;
      xa--;
      xb++;
    }
  while (--n);

  *(*out)++ = (float) (l0+h*(r0-l0));
  if (stereo) *(*out)++ = (float) (l1+h*(r1-l1));
  (*out) += skip;
}

static void ctp1_1(complex *x,real *tp1,real *wtab,long fm)
{
  register long	i,fn2;

  fn2=1<<(fm-1);
  r4fft(wtab,(real *) x,fm);
  for (i=0; i<fn2; i++)
    {
      x[i+fn2].re*=tp1[fn2-i];
      x[i+fn2].im*=tp1[fn2-i];
      x[i    ].re*=tp1[i    ];
      x[i    ].im*=tp1[i    ];
    }
  r4ifft(wtab,(real *) x,fm);
}

static void ctp1_2(complex *x,real *tp1,real *wtab,long fm)
{
  register long	i,fn2;

  fn2=1<<(fm-1);

  r4fft(wtab+(1<<fm),(real *) x,fm-1);
  for (i=0; i<fn2; i++)
    {
      x[i+fn2].re=x[i].re*tp1[fn2-i];
      x[i+fn2].im=x[i].im*tp1[fn2-i];
      x[i].re*=tp1[i];
      x[i].im*=tp1[i];
    }
  r4ifft(wtab,(real *) x,fm);
}


/* ---------- functions ---------- */

/* RateConvInit() */
/* Init audio sample rate conversion. */

RCBuf *RateConvInit (
  int debugLevel,		/* in: debug level */
				/*     0=off  1=basic  2=full */
  double ratio,			/* in: outputRate / inputRate */
  double fracOff,		/* in: inital fractional offset (0..1) */
				/*      i.e. position of 1st output sample */
				/*      on inputRate grid (dflt.: 0) */
  int numChannel,		/* in: number of channels */
  int htaps1,			/* in: num taps */
				/*      -1 = auto */
  float a1,			/* in: alpha for Kaiser window */
				/*      -1 = auto */
  float fc,			/* in: 6dB cutoff freq / input bandwidth */
				/*      -1 = auto */
  float fd,			/* in: 100dB cutoff freq / input bandwidth */
				/*      -1 = auto */
  long *numSampleIn)		/* out: num input samples / frame */
				/* returns: */
				/*  buffer (handle) */
				/*  or NULL if error */
{
  real *wtab;
  complex *x;
  real *tp1;
  real *tp2;
  complex *ibuf;

  long nfn;
  long adv;
  long istart;
  long numode;
  long fm;
  double p1;
  double d1;

  double sf2;
  double trw2;
  long fn;
  long fn2;
  long i;
  double h;
  double a;

  long chanpair,j;

  RCBuf *buf;

  RCdebugLevel = debugLevel;
  if (RCdebugLevel)
    printf("RateConvInit: debugLevel=%d ratio=%f fracOff=%f numChannel=%d\n"
	   "htaps1=%d a1=%f fc=%f fd=%f\n",
	   RCdebugLevel,ratio,fracOff,numChannel,htaps1,a1,fc,fd);

  if (!(buf=(RCBuf*)malloc(sizeof(RCBuf))))
    CommonExit(-1,"RateConvInit: Can not allocate memory");
  buf->currentSampleIn = 0;
  buf->currentSampleOut = 0;
  buf->numChannel = numChannel;
  chanpair = (buf->numChannel+1)/2;

  if (htaps1<0)
    htaps1 = deftaps1;
  htaps1 /= 2;	/* NM 991104 */
  if (a1<0)
    a1 = 10.0;
  numode = 0;
  for (fm=2; (1<<fm)<8*htaps1; fm+=2);
  fn=1<<fm;
  fn2=fn/2;

  if (!(wtab=
	(real *) malloc((4*fn + fn2+1 + (htaps2+2)*ov2fac)*sizeof(real))))
    CommonExit(-1,"RateConvInit: Can not allocate memory");
  x=((complex *) wtab)+fn;
  tp1=(real *) (x+fn);
  tp2=tp1+fn2+1;

  mkeiwutab(wtab,fn);
  mktp1(x,0.5,a1,fn,htaps1);
  r4fft(wtab,(real *) x,fm);
  h=x[0].re*x[0].re*1.0e-10;
  for (i=fn/2; x[i].re*x[i].re<h; i--);
  trw2=((double) (i-(fn2/2)))/((double) (fn2/2));

  sf2 = ratio;
  if ( sf2<0.0 ) sf2=1.0;

  d1=1.0/sf2;

  if ( fc<0.0 )
    {
      if ( fd<0.0 )
	{
	  fc=(float)(1.0/d1-trw2);
	  if ( fc<0.5-0.5*trw2 ) { numode=1; fc=(float)(2.0/d1-trw2); }
	  if ( fc>1.0-trw2 ) fc=(float)(1.0-trw2);
	}
      else
	{
	  fc=(float)(fd-trw2);
	  if ( fd<=0.5 ) { numode=1; fc=(float)(2.0*fd-trw2); }
	}
    }
  else
    {
      if ( fc<0.5-trw2 ) { numode=1; fc+=fc; }
    }

  if ( fc>2.0 ) fc=2.0;

  if ( fc<=0.0 ) {
    CommonWarning("RateConvInit: cutoff frequency to low: %f %f %f\n",
		  fc,fd,trw2);
    free(wtab);
    free(buf);
    return NULL;
  }

  nfn=fn;
  adv=fn-2*(htaps1+htaps2+2);
  istart=htaps1+htaps2+2;
  if ( !numode ) { nfn>>=1; adv>>=1; d1+=d1; fracOff+=fracOff; }

  p1=htaps1+htaps2+2+fracOff;		/* delay compensation */

  mktp1(x,0.5*fc,a1,fn,htaps1);
  r4fft(wtab,(real *) x,fm);
  a=1.0/((double) (((1+numode)*fn2)));
  for (i=0; i<=fn2 ; i++) tp1[i]=a*x[i].re;

  mktp2(tp2,cutoff2,alpha2);

  *numSampleIn = adv*max(2,buf->numChannel);
  buf->outSize = (long)((2*adv+4)*ratio+4);
  if (buf->numChannel>1)
    buf->outSize = (buf->outSize/2)*buf->numChannel;
  if (!(buf->out=(float*)malloc(buf->outSize*sizeof(float))))
    CommonExit(-1,"RateConvInit: Can not allocate memory");

  if (!(ibuf = (complex *) malloc((nfn-adv)*chanpair*sizeof(complex))))
    CommonExit(-1,"RateConvInit: Can not allocate memory");

  buf->wtab = wtab;
  buf->x= x;
  buf->tp1 = tp1;
  buf->tp2 = tp2;
  buf->ibuf = ibuf;

  buf->nfn = nfn;
  buf->adv = adv;
  buf->istart = istart;
  buf->numode = numode;
  buf->fm = fm;
  buf->p1 = p1;
  buf->d1 = d1;

  if ( buf->numChannel==1 ) {
    /* ibuf[?].im not used ... */
    for (i=0; i<(nfn-adv)/2; i++)
      ibuf[i].re=0.0;
    for (   ; i<(nfn-adv)  ; i++)
      ibuf[i].re=0.0;
    /* ibuf[i].re=(real) dataIn[idxIn++]; */
  }
  else {
    for (j=0; j<chanpair; j++) {
      for (i=0; i<  (nfn-adv); i++)
	((real *) ibuf)[i]=0.0;
      for (   ; i<2*(nfn-adv); i++)
	((real *) ibuf)[i]=0.0;
      /* ((real *) ibuf)[i]=(real) dataIn[idxIn++]; */
      ibuf += nfn-adv;
    }
  }

  if (RCdebugLevel)
    printf("RateConvInit: inSize=%ld outSize=%ld\n"
	   "  output/input ratio : %8.2f\n"
	   "  cutoff frequency   : %10.4f\n"
	   "  Kaiser parameter   : %10.4f\n"
	   "  filter 1 taps      : %5li\n"
	   "  filter 1 transition: %10.4f\n"
	   "  FFT length         : %5li\n"
	   "  oversampling factor: %5li\n"
	   "  nfn/numode         : %5li/%li\n",
	   2*adv,buf->outSize,
	   sf2,fc/(1+numode),a1,(long)2*htaps1+1,0.5*trw2,
	   fn,ov2fac*(2-numode),nfn,numode);

  return buf;
}


/* RateConv() */
/* Convert sample rate for one frame of audio data. */

long RateConv (
  RCBuf *buf,			/* in: buffer (handle) */
  float *dataIn,		/* in: input data[] */
  long numSampleIn,		/* in: number of input samples */
  float **dataOut)		/* out: output data[] */
				/* returns: */
				/*  numSampleOut */
{
  real *wtab = buf->wtab;
  complex *x = buf->x;
  real *tp1 = buf->tp1;
  real *tp2 = buf->tp2;
  complex *ibuf = buf->ibuf;

  long nfn = buf->nfn;
  long adv = buf->adv;
  long istart = buf->istart;
  long numode = buf->numode;
  long fm = buf->fm;
  double p1 = buf->p1;
  double d1 = buf->d1;

  long i;
  double p,h;
  long chanpair,j;

  long idxIn,numOut;
  float *out;

  if (RCdebugLevel)
    printf("RateConv: numSampleIn=%ld ",numSampleIn);

  if (numSampleIn != adv*max(2,buf->numChannel))
    CommonExit(-1,"RateConv: wrong numSampleIn");

  if ( buf->numChannel==1 ) {
    idxIn = 0;
    out = buf->out;

    /* ibuf[?].im not used ... */
    for (i=0      ; i<nfn-adv; i++)
      x[i].re=ibuf[i].re;
    for (         ; i<    adv; i++)
      x[i].re=(real) dataIn[idxIn++];
    for (         ; i<    nfn; i++)
      x[i].re=x[i-adv].im=(real) dataIn[idxIn++];
    for (i=nfn-adv; i<    adv; i++)
      x[i].im=(real) dataIn[idxIn++];
    for (         ; i<    nfn; i++)
      x[i].im=ibuf[i-adv].re=(real) dataIn[idxIn++];
      
    if ( numode )
      ctp1_1(x,tp1,wtab,fm);
    else
      ctp1_2(x,tp1,wtab,fm);
      
    i=adv;
    h=(double) ((2*i)>>numode);
    for (p=p1; p<h; p+=d1) ctp2_m(&(x[istart].re),p,tp2,&out);
    p1=p-h;
      
    i=adv;
    h=(double) ((2*i)>>numode);
    for (p=p1; p<h; p+=d1) ctp2_m(&(x[istart].im),p,tp2,&out);
    p1=p-h;
  }
  else {
    chanpair = buf->numChannel/2;
    for (j=0; j<chanpair; j++) {
      idxIn = 2*j;
      out = buf->out+2*j;
      for (i=0; i<nfn-adv; i++)
	x[i]=ibuf[i];
      for (; i<adv; i++) {
	x[i].re=(real) dataIn[idxIn++];
	x[i].im=(real) dataIn[idxIn++];
	idxIn += buf->numChannel-2;
      }
      for (; i<nfn; i++) {
	x[i].re=ibuf[i-adv].re=(real) dataIn[idxIn++];
	x[i].im=ibuf[i-adv].im=(real) dataIn[idxIn++];
	idxIn += buf->numChannel-2;
      }
      
      if ( numode )
	ctp1_1(x,tp1,wtab,fm);
      else
	ctp1_2(x,tp1,wtab,fm);
      
      i=2*adv;
      h=(double) (i>>numode);
      for (p=p1; p<h; p+=d1) ctp2_s(x+istart,p,tp2,&out,1,buf->numChannel-2);
      out -= buf->numChannel-2;
      ibuf += nfn-adv;
    }
    if (buf->numChannel%2) {
      idxIn = 2*j;
      out = buf->out+2*j;
      for (i=0; i<nfn-adv; i++)
	x[i]=ibuf[i];
      for (; i<adv; i++) {
	x[i].re=(real) dataIn[idxIn++];
	x[i].im=(real) 0.0;
	idxIn += buf->numChannel-1;
      }
      for (; i<nfn; i++) {
	x[i].re=ibuf[i-adv].re=(real) dataIn[idxIn++];
	x[i].im=ibuf[i-adv].im=(real) 0.0;
	idxIn += buf->numChannel-1;
      }
      
      if ( numode )
	ctp1_1(x,tp1,wtab,fm);
      else
	ctp1_2(x,tp1,wtab,fm);
      
      i=2*adv;
      h=(double) (i>>numode);
      for (p=p1; p<h; p+=d1) ctp2_s(x+istart,p,tp2,&out,0,buf->numChannel-1);
      out -= buf->numChannel-1;
    }
    p1=p-h;
  }

  buf->p1 = p1;

  *dataOut = buf->out;
  numOut = out - buf->out;
  if (numOut > buf->outSize)
    CommonExit(-1,"RateConv: output buffer size troubles");
  buf->currentSampleIn += idxIn;
  buf->currentSampleOut += numOut;
  
  if (RCdebugLevel)
    printf("numSampleOut=%ld\n",numOut);

  return numOut;
}


/* RateConvFree() */
/* Free RateConv buffers. */

void RateConvFree (
  RCBuf *buf)			/* in: buffer (handle) */
{
  if (RCdebugLevel)
    printf("RateConvFree: sampleIn=%ld sampleOut=%ld\n",
	   buf->currentSampleIn,buf->currentSampleOut);
  free(buf->out);
  free(buf->wtab);
  free(buf->ibuf);
  free(buf);
}


/* end of rateconv.c */

