view procedures.cpp @ 1:6422640a802f

first upload
author Wen X <xue.wen@elec.qmul.ac.uk>
date Tue, 05 Oct 2010 10:45:57 +0100
parents
children fc19d45615d1
line wrap: on
line source
//---------------------------------------------------------------------------

#include <math.h>
#include <mem.h>
#include "procedures.h"
#include "matrix.h"
#include "opt.h"
#include "SinEst.h"

//---------------------------------------------------------------------------
//TGMM methods

//method TGMM::TGMM: default constructor
TGMM::TGMM()
{
  p=0, m=dev=0;
}//TGMM

//method GMM:~TGMM: default destructor
TGMM::~TGMM()
{
  ReleaseGMM(p, m, dev)
};

//---------------------------------------------------------------------------
//TFSpans methods

//method TTFSpans: default constructor
TTFSpans::TTFSpans()
{
  Count=0;
  Capacity=100;
  Items=new TTFSpan[Capacity];
}//TTFSpans

//method ~TTFSpans: default destructor
TTFSpans::~TTFSpans()
{
  delete[] Items;
}//~TTFSpans

/*
  method Add: add a new span to the list

  In: ATFSpan: the new span to add
*/
void TTFSpans::Add(TTFSpan& ATFSpan)
{
  if (Count==Capacity)
  {
    int OldCapacity=Capacity;
    Capacity+=50;
    TTFSpan* NewItems=new TTFSpan[Capacity];
    memcpy(NewItems, Items, sizeof(TTFSpan)*OldCapacity);
    delete[] Items;
    Items=NewItems;
  }
  Items[Count]=ATFSpan;
  Count++;
}//Add

/*
  method Clear: discard the current content without freeing memory.
*/
void TTFSpans::Clear()
{
  Count=0;
}//Clear

/*
  method Delete: delete a span from current list

  In: Index: index to the span to delete
*/
int TTFSpans::Delete(int Index)
{
  if (Index<0 || Index>=Count)
    return 0;
  memmove(&Items[Index], &Items[Index+1], sizeof(TTFSpan)*(Count-1-Index));
  Count--;
  return 1;
}//Delete

//---------------------------------------------------------------------------
//SpecTrack methods

/*
  method TSpecTrack::Add: adds a SpecPeak to the track.

  In: APeak: the SpecPeak to add.
*/
int TSpecTrack::Add(TSpecPeak& APeak)
{
  if (Count>=Capacity)
  {
    Peaks=(TSpecPeak*)realloc(Peaks, sizeof(TSpecPeak)*(Capacity*2));
    Capacity*=2;
  }
  int ind=LocatePeak(APeak);
  if (ind<0)
  {
    InsertPeak(APeak, -ind-1);
    ind=-ind-1;
  }

  int t=APeak.t;
  double f=APeak.f;
  if (Count==1) t1=t2=t, fmin=fmax=f;
  else
  {
    if (t<t1) t1=t;
    else if (t>t2) t2=t;
    if (f<fmin) fmin=f;
    else if (f>fmax) fmax=f;
  }
  return ind;
}//Add

/*
  method TSpecTrack::TSpecTrack: creates a SpecTrack with an inital capacity.

  In: ACapacity: initial capacity, i.e. the number SpecPeak's to allocate storage space for.
*/
TSpecTrack::TSpecTrack(int ACapacity)
{
  Count=0;
  Capacity=ACapacity;
  Peaks=new TSpecPeak[Capacity];
}//TSpecTrack

//method TSpecTrack::~TSpecTrack: default destructor.
TSpecTrack::~TSpecTrack()
{
  delete[] Peaks;
}//TSpecTrack

/*
  method InsertPeak: inserts a new SpecPeak into the track at a given index. Internal use only.

  In: APeak: the SpecPeak to insert.
      index: the position in the list to place the new SpecPeak. Original SpecPeak's at and after this
        position are shifted by 1 posiiton.
*/
void TSpecTrack::InsertPeak(TSpecPeak& APeak, int index)
{
  memmove(&Peaks[index+1], &Peaks[index], sizeof(TSpecPeak)*(Count-index));
  Peaks[index]=APeak;
  Count++;
}//InsertPeak

/*
  method TSpecTrack::LocatePeak: looks for a SpecPeak in the track that has the same time (t) as APeak.

  In: APeak: a SpecPeak

  Returns the index in this track of the first SpecPeak that has the same time stamp as APeak. However,
  if there is no peak with that time stamp, the method returns -1 if APeaks comes before the first
  SpecPeak, -2 if between 1st and 2nd SpecPeak's, -3 if between 2nd and 3rd SpecPeak's, etc.
*/
int TSpecTrack::LocatePeak(TSpecPeak& APeak)
{
  if (APeak.t<Peaks[0].t) return -1;
  if (APeak.t>Peaks[Count-1].t) return -Count-1;

  if (APeak.t==Peaks[0].t) return 0;
  else if (APeak.t==Peaks[Count-1].t) return Count-1;

  int a=0, b=Count-1, c=(a+b)/2;
  while (a<c)
  {
    if (APeak.t==Peaks[c].t) return c;
    else if (APeak.t<Peaks[c].t) {b=c; c=(a+b)/2;}
    else {a=c; c=(a+b)/2;}
  }
  return -a-2;
}//LocatePeak

//---------------------------------------------------------------------------
/*
  function: ACPower: AC power

  In: data[Count]: a signal

  Returns the power of its AC content.
*/
double ACPower(double* data, int Count, void*)
{
  if (Count<=0) return 0;
  double power=0, avg=0, tmp;
  for (int i=0; i<Count; i++)
  {
    tmp=*(data++);
    power+=tmp*tmp;
    avg+=tmp;
  }
  power=(power-avg*avg)/Count;
  return power;
}//ACPower

//---------------------------------------------------------------------------
/*
  function Add: vector addition

  In: dest[Count], source[Count]: two vectors
  Out: dest[Count]: their sum

  No return value.
*/
void Add(double* dest, double* source, int Count)
{
  for (int i=0; i<Count; i++) *(dest++)+=*(source++);
}//Add

/*
  function Add: vector addition

  In: addend[count], adder[count]: two vectors
  Out: out[count]: their sum

  No return value.
*/
void Add(double* out, double* addend, double* adder, int count)
{
  for (int i=0; i<count; i++) *(out++)=*(addend++)+*(adder++);
}//Add

//---------------------------------------------------------------------------

/*
  function ApplyWindow: applies window function to signal buffer.

  In: Buffer[Size]: signal to be windowed
      Weight[Size]: the window
  Out: OutBuffer[Size]: windowed signal

  No return value;
*/
void ApplyWindow(double* OutBuffer, double* Buffer, double* Weights, int Size)
{
  for (int i=0; i<Size; i++) *(OutBuffer++)=*(Buffer++)**(Weights++);
}//ApplyWindow

//---------------------------------------------------------------------------
/*
  function Avg: average

  In: data[Count]: a data array

  Returns the average of the array.
*/
double Avg(double* data, int Count, void*)
{
  if (Count<=0) return 0;
  double avg=0;
  for (int i=0; i<Count; i++) avg+=*(data++);
  avg/=Count;
  return avg;
}//Avg

//---------------------------------------------------------------------------
/*
  function AvgFilter: get slow-varying wave trace by averaging

  In: data[Count]: input signal
      HWid: half the size of the averaging window
  Out: datout[Count]: the slow-varying part of data[].

  No return value.
*/
void AvgFilter(double* dataout, double* data, int Count, int HWid)
{
  double sum=0;

  dataout[0]=data[0];

  for (int i=1; i<=HWid; i++)
  {
    sum+=data[2*i-1]+data[2*i];
    dataout[i]=sum/(2*i+1);
  }

  for (int i=HWid+1; i<Count-HWid; i++)
  {
    sum=sum+data[i+HWid]-data[i-HWid-1];
    dataout[i]=sum/(2*HWid+1);
  }

  for (int i=Count-HWid; i<Count; i++)
  {
    sum=sum-data[2*i-Count-1]-data[2*i-Count];
    dataout[i]=sum/(2*(Count-i)-1);
  }
}//AvgFilter

//---------------------------------------------------------------------------
/*
  function CalculateSpectrogram: computes the spectrogram of a signal

  In: data[Count]: the time-domain signal
      start, end: start and end points marking the section for which the spectrogram is to be computed
      Wid, Offst: frame size and hop size
      Window: window function
      amp: a pre-amplifier
      half: specifies if the spectral values at Wid/2 are to be retried
  Out: Spec[][Wid/2] or Spec[][Wid/2+1]: amplitude spectrogram
       ph[][][Wid/2] or Ph[][Wid/2+1]: phase spectrogram

  No return value. The caller is repsonse to arrange storage spance of output buffers.
*/
void CalculateSpectrogram(double* data, int Count, int start, int end, int Wid, int Offst, double* Window, double** Spec, double** Ph, double amp, bool half)
{
  AllocateFFTBuffer(Wid, fft, w, x);

  int Fr=(end-start-Wid)/Offst+1;

  for (int i=0; i<Fr; i++)
  {
    RFFTCW(&data[i*Offst+start], Window, 0, 0, log2(Wid), w, x);

    if (Spec)
    {
      for (int j=0; j<Wid/2; j++)
        Spec[i][j]=sqrt(x[j].x*x[j].x+x[j].y*x[j].y)*amp;
      if (half)
        Spec[i][Wid/2]=sqrt(x[Wid/2].x*x[Wid/2].x+x[Wid/2].y*x[Wid/2].y)*amp;
    }
    if (Ph)
    {
      for (int j=0; j<=Wid/2; j++)
        Ph[i][j]=Atan2(x[j].y, x[j].x);
      if (half)
        Ph[i][Wid/2]=Atan2(x[Wid/2].y, x[Wid/2].x);
    }
  }
  FreeFFTBuffer(fft);
}//CalculateSpectrogram

//---------------------------------------------------------------------------
/*
  function Conv: simple convolution

  In: in1[N1], in2[N2]: two sequences
  Out: out[N1+N2-1]: their convolution

  No return value.
*/
void Conv(double* out, int N1, double* in1, int N2, double* in2)
{
  int N=N1+N1-1;
  memset(out, 0, sizeof(double)*N);
  for (int n1=0; n1<N1; n1++)
    for (int n2=0; n2<N2; n2++)
      out[n1+n2]+=in1[n1]*in2[n2];
}//Conv

//---------------------------------------------------------------------------
/*
  function Correlation: computes correlation coefficient of 2 vectors a & b, equals cos(aOb).

  In: a[Count], b[Count]: two vectors

  Returns their correlation coefficient.
*/
double Correlation(double* a, double* b, int Count)
{
  double aa=0, bb=0, ab=0;
  for (int i=0; i<Count; i++)
  {
    aa+=*a**a;
    bb+=*b**b;
    ab+=*(a++)**(b++);
  }
  return ab/sqrt(aa*bb);
}//Correlation

//---------------------------------------------------------------------------
/*
  function DCAmplitude: DC amplitude

  In: data[Count]: a signal

  Returns its DC amplitude (=AC amplitude without DC removing)
*/
double DCAmplitude(double* data, int Count, void*)
{
  if (Count<=0) return 0;
  double power=0, tmp;
  for (int i=0; i<Count; i++)
  {
    tmp=*(data++);
    power+=tmp*tmp;
  }
  power/=Count;
  return sqrt(2*power);
}//DCAmplitude

/*
  function DCPower: DC power

  In: data[Count]: a signal

  Returns its DC power.
*/
double DCPower(double* data, int Count, void*)
{
  if (Count<=0) return 0;
  double power=0, tmp;
  for (int i=0; i<Count; i++)
  {
    tmp=*(data++);
    power+=tmp*tmp;
  }
  power/=Count;
  return power;
}//DCPower

//---------------------------------------------------------------------------
/*
  DCT: discrete cosine transform, direct computation. For fast DCT, see fft.cpp.

  In: input[N]: a signal
  Out: output[N]: its DCT

  No return value.
*/
void DCT( double* output, double* input, int N)
{
  double Wn;

  for (int n=0; n<N; n++)
  {
    output[n]=0;
    Wn=n*M_PI/2/N;
    for (int k=0; k<N; k++)
      output[n]+=input[k]*cos((2*k+1)*Wn);
    if (n==0) output[n]*=1.4142135623730950488016887242097/N;
    else output[n]*=2.0/N;
  }
}//DCT

/*
  function IDCT: inverse discrete cosine transform, direct computation. For fast IDCT, see fft.cpp.

  In: input[N]: a signal
  Out: output[N]: its IDCT

  No return value.
*/
void IDCT(double* output, double* input, int N)
{
  for (int k=0; k<N; k++)
  {
    double Wk=(2*k+1)*M_PI/2/N;
    output[k]=input[0]/1.4142135623730950488016887242097;
    for (int n=1; n<N; n++)
      output[k]+=input[n]*cos(n*Wk);
  }
}//IDCT

//---------------------------------------------------------------------------
/*
  function DeDC: removes DC component of a signal

  In: data[Count]: the signal
      HWid: half of averaging window size
  Out: data[Count]: de-DC-ed signal

  No return value.
*/
void DeDC(double* data, int Count, int HWid)
{
  double* data2=new double[Count];
  AvgFilter(data2, data, Count, HWid);
  for (int i=0; i<Count; i++)
    *(data++)-=*(data2++);
  delete[] data2;
}//DeDC

/*
  function DeDC_static: removes DC component statically

  In: data[Count]: the signal
  Out: data[Count]: DC-removed signal

  No return value.
*/
void DeDC_static(double* data, int Count)
{
  double avg=Avg(data, Count, 0);
  for (int i=0; i<Count; i++) *(data++)-=avg;
}//DeDC_static

//---------------------------------------------------------------------------
/*
  function DoubleToInt: converts double-precision floating point array to integer array

  In: in[Count]: the double array
      BytesPerSample: bytes per sample of destination integers
  Out: out[Count]: the integer array

  No return value.
*/
void DoubleToInt(void* out, int BytesPerSample, double* in, int Count)
{
  if (BytesPerSample==1){unsigned char* out8=(unsigned char*)out; for (int k=0; k<Count; k++) *(out8++)=*(in++)+128.5;}
  else {__int16* out16=(__int16*)out; for (int k=0; k<Count; k++) *(out16++)=floor(*(in++)+0.5);}
}//DoubleToInt

/*
  function DoubleToIntAdd: adds double-precision floating point array to integer array

  In: in[Count]: the double array
      out[Count]: the integer array
      BytesPerSample: bytes per sample of destination integers
  Out: out[Count]: the sum of the two arrays

  No return value.
*/
void DoubleToIntAdd(void* out, int BytesPerSample, double* in, int Count)
{
  if (BytesPerSample==1)
  {
    unsigned char* out8=(unsigned char*)out;
    for (int k=0; k<Count; k++){*out8=*out8+*in+128.5; out8++; in++;}
  }
  else
  {
    __int16* out16=(__int16*)out;
    for (int k=0; k<Count; k++){*out16=*out16+floor(*in+0.5); out16++; in++;}
  }
}//DoubleToIntAdd

//---------------------------------------------------------------------------
/*
  DPower: in-frame power variation

  In: data[Count]: a signal

  returns the different between AC powers of its first and second halves.
*/
double DPower(double* data, int Count, void*)
{
  double ene1=ACPower(data, Count/2, 0);
  double ene2=ACPower(&data[Count/2], Count/2, 0);
  return ene2-ene1;
}//DPower

//---------------------------------------------------------------------------
/*
  funciton Energy: energy

  In: data[Count]: a signal

  Returns its total energy
*/
double Energy(double* data, int Count)
{
  double result=0;
  for (int i=0; i<Count; i++) result+=data[i]*data[i];
  return result;
}//Energy

//---------------------------------------------------------------------------
/*
  function ExpOnsetFilter: onset filter with exponential impulse response h(t)=Aexp(-t/Tr)-Bexp(-t/Ta),
  A=1-exp(-1/Tr), B=1-exp(-1/Ta).

  In: data[Count]: signal to filter
      Tr, Ta: time constants of h(t)
  Out: dataout[Count]: filtered signal, normalized by multiplying a factor.

  Returns the normalization factor. Identical data and dataout is allowed.
*/
double ExpOnsetFilter(double* dataout, double* data, int Count, double Tr, double Ta)
{
  double FA=0, FB=0;
  double EA=exp(-1.0/Tr), EB=exp(-1.0/Ta);
  double A=1-EA, B=1-EB;
  double NormFactor=1/sqrt((1-EA)*(1-EA)/(1-EA*EA)+(1-EB)*(1-EB)/(1-EB*EB)-2*(1-EA)*(1-EB)/(1-EA*EB));
  for (int i=0; i<Count; i++)
  {
    FA=FA*EA+*data;
    FB=FB*EB+*(data++);
    *(dataout++)=(A*FA-B*FB)*NormFactor;
  }
  return NormFactor;
}//ExpOnsetFilter

/*
  function ExpOnsetFilter_balanced: exponential onset filter without starting step response. It
  extends the input signal at the front end by bal*Ta samples by repeating the its value at 0, then
  applies the onset filter on the extended signal instead.

  In: data[Count]: signal to filter
      Tr, Ta: time constants to the impulse response of onset filter, see ExpOnsetFilter().
      bal: balancing factor
  Out: dataout[Count]: filtered signal, normalized by multiplying a factor.

  Returns the normalization factor. Identical data and dataout is allowed.
*/
double ExpOnsetFilter_balanced(double* dataout, double* data, int Count, double Tr, double Ta, int bal)
{
  double* tmpdata=new double[int(Count+bal*Ta)];
  double* ltmpdata=tmpdata;
  for (int i=0; i<bal*Ta; i++) *(ltmpdata++)=data[0];
  memcpy(ltmpdata, data, sizeof(double)*Count);
  double result=ExpOnsetFilter(tmpdata, tmpdata, bal*Ta+Count, Tr, Ta);
  memcpy(dataout, ltmpdata, sizeof(double)*Count);
  delete[] tmpdata;
  return result;
}//ExpOnsetFilter_balanced

//---------------------------------------------------------------------------
/*
  function ExtractLinearComponent: Legendre linear component

  In: data[Count+1]: a signal
  Out: dataout[Count+1]: its Legendre linear component, optional.

  Returns the coefficient to the linear component.
*/
double ExtractLinearComponent(double* dataout, double* data, int Count)
{
  double tmp=0;
  int N=Count*2;
  for (int n=0; n<=Count; n++) tmp+=n**(data++);
  tmp=tmp*24/N/(N+1)/(N+2);
  if (dataout)
    for (int n=0; n<=Count; n++) *(dataout++)=tmp*n;
  return tmp;
}//ExtractLinearComponent

//---------------------------------------------------------------------------
/*
  function FFTConv: fast convolution of two series by FFT overlap-add. In an overlap-add scheme it is
  assumed that one of the convolvends is short compared to the other one, which can be potentially
  infinitely long. The long convolvend is devided into short segments, each of which is convolved with
  the short convolvend, the results of which are then assembled into the final result. The minimal delay
  from input to output is the amount of overlap, which is the size of the short convolvend minus 1.

  In: source1[size1]: convolvend
      source2[size2]: second convolvend
      zero: position of first point in convoluton result, relative to main output buffer.
      pre_buffer[-zero]: buffer hosting values to be overlap-added to the start of the result.
  Out: dest[size1]: the middle part of convolution result
       pre_buffer[-zero]: now updated by adding beginning part of the convolution result
       post_buffer[size2+zero]: end part of the convolution result

  No return value. Identical dest and source1 allowed.

  The convolution result has length size1+size2 (counting one trailing zero). If zero lies in the range
  between -size2 and 0, then the first -zero samples are added to pre_buffer[], next size1 samples are
  saved to dest[], and the last size2+zero sampled are saved to post_buffer[]; if not, the middle size1
  samples are saved to dest[], while pre_buffer[] and post_buffer[] are not used.
*/
void FFTConv(double* dest, double* source1, int size1, double* source2, int size2, int zero, double* pre_buffer, double* post_buffer)
{
  int order=log2(size2-1)+1+1;
  int Wid=1<<order;
  int HWid=Wid/2;
  int Fr=size1/HWid;
  int res=size1-HWid*Fr;
  bool trunc=false;
  if (zero<-size2+1 || zero>0)  zero=-size2/2, trunc=true;
  if (pre_buffer==NULL || (post_buffer==NULL && size2+zero!=0)) trunc=true;

  AllocateFFTBuffer(Wid, fft, w, x1);
  int* hbitinv=CreateBitInvTable(order-1);
  cdouble* x2=new cdouble[Wid];
  double* tmp=new double[HWid];
  memset(tmp, 0, sizeof(double)*HWid);

  memcpy(fft, source2, sizeof(double)*size2);
  memset(&fft[size2], 0, sizeof(double)*(Wid-size2));
  RFFTC(fft, 0, 0, order, w, x2, hbitinv);

  double r1, r2, i1, i2;
  int ind, ind_;
  for (int i=0; i<Fr; i++)
  {
    memcpy(fft, &source1[i*HWid], sizeof(double)*HWid);
    memset(&fft[HWid], 0, sizeof(double)*HWid);

    RFFTC(fft, 0, 0, order, w, x1, hbitinv);

    for (int j=0; j<Wid; j++)
    {
      r1=x1[j].x, r2=x2[j].x, i1=x1[j].y, i2=x2[j].y;
      x1[j].x=r1*r2-i1*i2;
      x1[j].y=r1*i2+r2*i1;
    }
    CIFFTR(x1, order, w, fft, hbitinv);
    for (int j=0; j<HWid; j++) tmp[j]+=fft[j];

    ind=i*HWid+zero; //(i+1)*HWid<=size1
    ind_=ind+HWid; //ind_=(i+1)*HWid+zero<=size1
    if (ind<0)
    {
      if (!trunc)
        memdoubleadd(pre_buffer, tmp, -ind);
      memcpy(dest, &tmp[-ind], sizeof(double)*(HWid+ind));
    }
    else
      memcpy(&dest[ind], tmp, sizeof(double)*HWid);
    memcpy(tmp, &fft[HWid], sizeof(double)*HWid);
  }

  if (res>0)
  {
    memcpy(fft, &source1[Fr*HWid], sizeof(double)*res);
    memset(&fft[res], 0, sizeof(double)*(Wid-res));

    RFFTC(fft, 0, 0, order, w, x1, hbitinv);

    for (int j=0; j<Wid; j++)
    {
      r1=x1[j].x, r2=x2[j].x, i1=x1[j].y, i2=x2[j].y;
      x1[j].x=r1*r2-i1*i2;
      x1[j].y=r1*i2+r2*i1;
    }
    CIFFTR(x1, order, w, fft, hbitinv);
    for (int j=0; j<HWid; j++)
      tmp[j]+=fft[j];

    ind=Fr*HWid+zero; //Fr*HWid=size1-res, ind=size1-res+zero<size1
    ind_=ind+HWid;  //ind_=size1 -res+zero+HWid
    if (ind<0)
    {
      if (!trunc)
        memdoubleadd(pre_buffer, tmp, -ind);
      memcpy(dest, &tmp[-ind], sizeof(double)*(HWid+ind));
    }
    else if (ind_>size1)
    {
      memcpy(&dest[ind], tmp, sizeof(double)*(size1-ind));
      if (!trunc && post_buffer)
      {
        if (ind_>size1+size2+zero)
          memcpy(post_buffer, &tmp[size1-ind], sizeof(double)*(size2+zero));
        else
          memcpy(post_buffer, &tmp[size1-ind], sizeof(double)*(ind_-size1));
      }
    }
    else
      memcpy(&dest[ind], tmp, sizeof(double)*HWid);
    memcpy(tmp, &fft[HWid], sizeof(double)*HWid);
    Fr++;
  }

  ind=Fr*HWid+zero;
  ind_=ind+HWid;

  if (ind<size1)
  {
    if (ind_>size1)
    {
      memcpy(&dest[ind], tmp, sizeof(double)*(size1-ind));
      if (!trunc && post_buffer)
      {
        if (ind_>size1+size2+zero)
          memcpy(post_buffer, &tmp[size1-ind], sizeof(double)*(size2+zero));
        else
          memcpy(post_buffer, &tmp[size1-ind], sizeof(double)*(ind_-size1));
      }
    }
    else
      memcpy(&dest[ind], tmp, sizeof(double)*HWid);
  }
  else //ind>=size1 => ind_>=size1+size2+zero
  {
    if (!trunc && post_buffer)
      memcpy(&post_buffer[ind-size1], tmp, sizeof(double)*(size1+size2+zero-ind));
  }

  FreeFFTBuffer(fft);
  delete[] x2;
  delete[] tmp;
  delete[] hbitinv;
}//FFTConv

/*
  function FFTConv: the simplified version using two output buffers instead of three. This is almost
  equivalent to setting zero=-size2 in the three-output-buffer version (so that post_buffer no longer
  exists), except that this version requires size2 (renamed HWid) be a power of 2, and pre_buffer point
  to the END of the storage (so that pre_buffer=dest automatically connects the two buffers in a
  continuous memory block).

  In: source1[size1]: convolvend
      source2[HWid]: second convolved, HWid be a power of 2
      pre_buffer[-HWid:-1]: buffer hosting values to be overlap-added to the start of the result.
  Out: dest[size1]: main output buffer, now hosting end part of the result (after HWid samples).
       pre_buffer[-HWid:-1]: now updated by added the start of the result

  No return value.
*/
void FFTConv(double* dest, double* source1, int size1, double* source2, int HWid, double* pre_buffer)
{
  int Wid=HWid*2;
  int order=log2(Wid);
  int Fr=size1/HWid;
  int res=size1-HWid*Fr;

  AllocateFFTBuffer(Wid, fft, w, x1);
  cdouble *x2=new cdouble[Wid];
  double *tmp=new double[HWid];
  int* hbitinv=CreateBitInvTable(order-1);

  memcpy(fft, source2, sizeof(double)*HWid);
  memset(&fft[HWid], 0, sizeof(double)*HWid);
  RFFTC(fft, 0, 0, order, w, x2, hbitinv);

  double r1, r2, i1, i2;
  for (int i=0; i<Fr; i++)
  {
    memcpy(fft, &source1[i*HWid], sizeof(double)*HWid);
    memset(&fft[HWid], 0, sizeof(double)*HWid);

    RFFTC(fft, 0, 0, order, w, x1, hbitinv);

    for (int j=0; j<Wid; j++)
    {
      r1=x1[j].x, r2=x2[j].x, i1=x1[j].y, i2=x2[j].y;
      x1[j].x=r1*r2-i1*i2;
      x1[j].y=r1*i2+r2*i1;
    }
    CIFFTR(x1, order, w, fft, hbitinv);

    if (i==0)
    {
      if (pre_buffer!=NULL)
      {
        double* destl=&pre_buffer[-HWid+1];
        for (int j=0; j<HWid-1; j++) destl[j]+=fft[j];
      }
    }
    else
    {
      for (int j=0; j<HWid-1; j++) tmp[j+1]+=fft[j];
      memcpy(&dest[(i-1)*HWid], tmp, sizeof(double)*HWid);
    }
    memcpy(tmp, &fft[HWid-1], sizeof(double)*HWid);
  }

  if (res>0)
  {
    if (Fr==0) memset(tmp, 0, sizeof(double)*HWid);

    memcpy(fft, &source1[Fr*HWid], sizeof(double)*res);
    memset(&fft[res], 0, sizeof(double)*(Wid-res));

    RFFTC(fft, 0, 0, order, w, x1, hbitinv);
    for (int j=0; j<Wid; j++)
    {
      r1=x1[j].x, r2=x2[j].x, i1=x1[j].y, i2=x2[j].y;
      x1[j].x=r1*r2-i1*i2;
      x1[j].y=r1*i2+r2*i1;
    }
    CIFFTR(x1, order, w, fft, hbitinv);

    if (Fr==0)
    {
      if (pre_buffer!=NULL)
      {
        double* destl=&pre_buffer[-HWid+1];
        for (int j=0; j<HWid-1; j++) destl[j]+=fft[j];
      }
    }
    else
    {
      for (int j=0; j<HWid-1; j++) tmp[j+1]+=fft[j];
      memcpy(&dest[(Fr-1)*HWid], tmp, sizeof(double)*HWid);
    }

    memcpy(&dest[Fr*HWid], &fft[HWid-1], sizeof(double)*res);
  }
  else
    memcpy(&dest[(Fr-1)*HWid], tmp, sizeof(double)*HWid);

  FreeFFTBuffer(fft);
  delete[] x2; delete[] tmp; delete[] hbitinv;
}//FFTConv

/*
  function FFTConv: fast convolution of two series by FFT overlap-add. Same as the three-output-buffer
  version above but using integer output buffers as well as integer source1.

  In: source1[size1]: convolvend
      bps: bytes per sample of integer units in source1[].
      source2[size2]: second convolvend
      zero: position of first point in convoluton result, relative to main output buffer.
      pre_buffer[-zero]: buffer hosting values to be overlap-added to the start of the result.
  Out: dest[size1]: the middle part of convolution result
       pre_buffer[-zero]: now updated by adding beginning part of the convolution result
       post_buffer[size2+zero]: end part of the convolution result

  No return value. Identical dest and source1 allowed.
*/
void FFTConv(unsigned char* dest, unsigned char* source1, int bps, int size1, double* source2, int size2, int zero, unsigned char* pre_buffer, unsigned char* post_buffer)
{
  int order=log2(size2-1)+1+1;
  int Wid=1<<order;
  int HWid=Wid/2;
  int Fr=size1/HWid;
  int res=size1-HWid*Fr;
  bool trunc=false;
  if (zero<-size2+1 || zero>0)  zero=-size2/2, trunc=true;
  if (pre_buffer==NULL || (post_buffer==NULL && size2+zero!=0)) trunc=true;

  AllocateFFTBuffer(Wid, fft, w, x1);
  cdouble* x2=new cdouble[Wid];
  double* tmp=new double[HWid];
  memset(tmp, 0, sizeof(double)*HWid);
  int* hbitinv=CreateBitInvTable(order-1);

  memcpy(fft, source2, sizeof(double)*size2);
  memset(&fft[size2], 0, sizeof(double)*(Wid-size2));
  RFFTC(fft, 0, 0, order, w, x2, hbitinv);

  double r1, r2, i1, i2;
  int ind, ind_;
  for (int i=0; i<Fr; i++)
  {
    IntToDouble(fft, &source1[i*HWid*bps], bps, HWid);
    memset(&fft[HWid], 0, sizeof(double)*HWid);

    RFFTC(fft, 0, 0, order, w, x1, hbitinv);

    for (int j=0; j<Wid; j++)
    {
      r1=x1[j].x, r2=x2[j].x, i1=x1[j].y, i2=x2[j].y;
      x1[j].x=r1*r2-i1*i2;
      x1[j].y=r1*i2+r2*i1;
    }
    CIFFTR(x1, order, w, fft, hbitinv);
    for (int j=0; j<HWid; j++) tmp[j]+=fft[j];

    ind=i*HWid+zero; //(i+1)*HWid<=size1
    ind_=ind+HWid; //ind_=(i+1)*HWid+zero<=size1
    if (ind<0)
    {
      if (!trunc)
        DoubleToIntAdd(pre_buffer, bps, tmp, -ind);
      DoubleToInt(dest, bps, &tmp[-ind], HWid+ind);
    }
    else
      DoubleToInt(&dest[ind*bps], bps, tmp, HWid);
    memcpy(tmp, &fft[HWid], sizeof(double)*HWid);
  }

  if (res>0)
  {
    IntToDouble(fft, &source1[Fr*HWid*bps], bps, res);
    memset(&fft[res], 0, sizeof(double)*(Wid-res));

    RFFTC(fft, 0, 0, order, w, x1, hbitinv);

    for (int j=0; j<Wid; j++)
    {
      r1=x1[j].x, r2=x2[j].x, i1=x1[j].y, i2=x2[j].y;
      x1[j].x=r1*r2-i1*i2;
      x1[j].y=r1*i2+r2*i1;
    }
    CIFFTR(x1, order, w, fft, hbitinv);
    for (int j=0; j<HWid; j++)
      tmp[j]+=fft[j];

    ind=Fr*HWid+zero; //Fr*HWid=size1-res, ind=size1-res+zero<size1
    ind_=ind+HWid;  //ind_=size1 -res+zero+HWid
    if (ind<0)
    {
      if (!trunc)
        DoubleToIntAdd(pre_buffer, bps, tmp, -ind);
      DoubleToInt(dest, bps, &tmp[-ind], HWid+ind);
    }
    else if (ind_>size1)
    {
      DoubleToInt(&dest[ind*bps], bps, tmp, size1-ind);
      if (!trunc && post_buffer)
      {
        if (ind_>size1+size2+zero)
          DoubleToInt(post_buffer, bps, &tmp[size1-ind], size2+zero);
        else
          DoubleToInt(post_buffer, bps, &tmp[size1-ind], ind_-size1);
      }
    }
    else
      DoubleToInt(&dest[ind*bps], bps, tmp, HWid);
    memcpy(tmp, &fft[HWid], sizeof(double)*HWid);
    Fr++;
  }

  ind=Fr*HWid+zero;
  ind_=ind+HWid;

  if (ind<size1)
  {
    if (ind_>size1)
    {
      DoubleToInt(&dest[ind*bps], bps, tmp, size1-ind);
      if (!trunc && post_buffer)
      {
        if (ind_>size1+size2+zero)
          DoubleToInt(post_buffer, bps, &tmp[size1-ind], size2+zero);
        else
          DoubleToInt(post_buffer, bps, &tmp[size1-ind], ind_-size1);
      }
    }
    else
      DoubleToInt(&dest[ind*bps], bps, tmp, HWid);
  }
  else //ind>=size1 => ind_>=size1+size2+zero
  {
    if (!trunc && post_buffer)
      DoubleToInt(&post_buffer[(ind-size1)*bps], bps, tmp, size1+size2+zero-ind);
  }

  FreeFFTBuffer(fft);
  delete[] x2;
  delete[] tmp;
  delete[] hbitinv;
}//FFTConv

//---------------------------------------------------------------------------
/*
  function FFTFilter: FFT with cosine-window overlap-add: This FFT filter is not an actural LTI system,
  but an block processing with overlap-add. In this function the blocks are overlapped by 50% and summed
  up with Hann windowing.

  In: data[Count]: input data
      Wid: DFT size
      On, Off: cut-off frequencies of FFT filter. On<Off: band-pass; On>Off: band-stop.
  Out: dataout[Count]: filtered data

  No return value. Identical data and dataout allowed
*/
void FFTFilter(double* dataout, double* data, int Count, int Wid, int On, int Off)
{
  int Order=log2(Wid);
  int HWid=Wid/2;
  int Fr=(Count-Wid)/HWid+1;
  AllocateFFTBuffer(Wid, ldata, w, x);

  double* win=new double[Wid];
  for (int i=0; i<Wid; i++) win[i]=sqrt((1-cos(2*M_PI*i/Wid))/2);
  double* tmpdata=new double[HWid];
  memset(tmpdata, 0, HWid*sizeof(double));

  for (int i=0; i<Fr; i++)
  {
    memcpy(ldata, &data[i*HWid], Wid*sizeof(double));
    if (i>0)
      for (int k=0; k<HWid; k++)
        ldata[k]=ldata[k]*win[k];
    for (int k=HWid; k<Wid; k++)
      ldata[k]=ldata[k]*win[k];

    RFFTC(ldata, NULL, NULL, Order, w, x, 0);

    if (On<Off) //band pass: keep [On, Off) and set other bins to zero
    {
      memset(x, 0, On*sizeof(cdouble));
      if (On>=1)
        memset(&x[Wid-On+1], 0, (On-1)*sizeof(cdouble));
      if (Off*2<=Wid)
        memset(&x[Off], 0, (Wid-Off*2+1)*sizeof(cdouble));
    }
    else //band stop: set [Off, On) to zero.
    {
      memset(&x[Off], 0, sizeof(cdouble)*(On-Off));
      memset(&x[Wid-On+1], 0, sizeof(double)*(On-Off));
    }

    CIFFTR(x, Order, w, ldata);

    if (i>0) for (int k=0; k<HWid; k++) ldata[k]=ldata[k]*win[k];
    for (int k=HWid; k<Wid; k++) ldata[k]=ldata[k]*win[k];

    memcpy(&dataout[i*HWid], tmpdata, HWid*sizeof(double));
    for (int k=0; k<HWid; k++) dataout[i*HWid+k]+=ldata[k];
    memcpy(tmpdata, &ldata[HWid], HWid*sizeof(double));
  }

  memcpy(&dataout[Fr*HWid], tmpdata, HWid*sizeof(double));
  memset(&dataout[Fr*HWid+HWid], 0, (Count-Fr*HWid-HWid)*sizeof(double));

  delete[] win;
  delete[] tmpdata;
  FreeFFTBuffer(ldata);
}//FFTFilter

/*
  funtion FFTFilterOLA: FFTFilter with overlap-add support. This is a true LTI filter whose impulse
  response is constructed using IFFT. The filtering is implemented by fast convolution.

  In: data[Count]: input data
      Wid: FFT size
      On, Off: cut-off frequencies, in bins, of the filter
      pre_buffer[Wid]: buffer hosting sampled to be added with the start of output
  Out: dataout[Count]: main output buffer, hosting the last $Count samples of output.
       pre_buffer[Wid]: now updated by adding the first Wid samples of output

  No return value. The complete output contains Count+Wid effective samples (including final 0); firt
  $Wid are added to pre_buffer[], next Count samples saved to dataout[].
*/
void FFTFilterOLA(double* dataout, double* data, int Count, int Wid, int On, int Off, double* pre_buffer)
{
  AllocateFFTBuffer(Wid, spec, w, x);
  memset(x, 0, sizeof(cdouble)*Wid);
  for (int i=On+1; i<Off; i++) x[i].x=x[Wid-i].x=1-2*(i%2);
  CIFFTR(x, log2(Wid), w, spec);
  FFTConv(dataout, data, Count, spec, Wid, -Wid, pre_buffer, NULL);
  FreeFFTBuffer(spec);
}//FFTFilterOLA
//version for integer input and output, where BytesPerSample specifies the integer format.
void FFTFilterOLA(unsigned char* dataout, unsigned char* data, int BytesPerSample, int Count, int Wid, int On, int Off, unsigned char* pre_buffer)
{
  AllocateFFTBuffer(Wid, spec, w, x);
  memset(x, 0, sizeof(cdouble)*Wid);
  for (int i=On+1; i<Off; i++) x[i].x=x[Wid-i].x=1-2*(i%2);
  CIFFTR(x, log2(Wid), w, spec);
  FFTConv(dataout, data, BytesPerSample, Count, spec, Wid, -Wid, pre_buffer, NULL);
  FreeFFTBuffer(spec);
}//FFTFilterOLA

/*
  function FFTFilterOLA: FFT filter with overlap-add support.

  In: data[Count]: input data
      amp[0:HWid]: amplitude response
      ph[0:HWid]: phase response, where ph[0]=ph[HWid]=0;
      pre_buffer[Wid]: buffer hosting sampled to be added to the beginning of the output
  Out: dataout[Count]: main output buffer, hosting the middle $Count samples of output.
       pre_buffer[Wid]: now updated by adding the first Wid/2 samples of output

  No return value.
*/
void FFTFilterOLA(double* dataout, double* data, int Count, double* amp, double* ph, int Wid, double* pre_buffer)
{
  int HWid=Wid/2;
  AllocateFFTBuffer(Wid, spec, w, x);
  x[0].x=amp[0], x[0].y=0;
  for (int i=1; i<HWid; i++)
  {
    x[i].x=x[Wid-i].x=amp[i]*cos(ph[i]);
    x[i].y=amp[i]*sin(ph[i]);
    x[Wid-i].y=-x[i].y;
  }
  x[HWid].x=amp[HWid], x[HWid].y=0;
  CIFFTR(x, log2(Wid), w, spec);
  FFTConv(dataout, data, Count, spec, Wid, -Wid, pre_buffer, NULL);
  FreeFFTBuffer(spec);
}//FFTFilterOLA

/*
  function FFTMask: masks a band of a signal with noise

  In: data[Count]: input signal
      DigiOn, DigiOff: cut-off frequences of the band to mask
      maskcoef: masking noise amplifier. If set to 1 than the mask level is set to the highest signal
        level in the masking band.
  Out: dataout[Count]: output data

  No return value.
*/
double FFTMask(double* dataout, double* data, int Count, int Wid, double DigiOn, double DigiOff, double maskcoef)
{
  int Order=log2(Wid);
  int HWid=Wid/2;
  int Fr=(Count-Wid)/HWid+1;
  int On=Wid*DigiOn, Off=Wid*DigiOff;
  AllocateFFTBuffer(Wid, ldata, w, x);

  double* winhann=new double[Wid];
  double* winhamm=new double[Wid];
  for (int i=0; i<Wid; i++)
    {winhamm[i]=0.54-0.46*cos(2*M_PI*i/Wid); winhann[i]=(1-cos(2*M_PI*i/Wid))/2/winhamm[i];}
  double* tmpdata=new double[HWid];
  memset(tmpdata, 0, HWid*sizeof(double));
  double max, randfi;

  max=0;
  for (int i=0; i<Fr; i++)
  {
    memcpy(ldata, &data[i*HWid], Wid*sizeof(double));
    if (i>0)
      for (int k=0; k<HWid; k++)
        ldata[k]=ldata[k]*winhamm[k];
    for (int k=HWid; k<Wid; k++)
      ldata[k]=ldata[k]*winhamm[k];

    RFFTC(ldata, ldata, NULL, Order, w, x, 0);

    for (int k=On; k<Off; k++)
    {
      x[k].x=x[Wid-k].x=x[k].y=x[Wid-k].y=0;
      if (max<ldata[k]) max=ldata[k];
    }

    CIFFTR(x, Order, w, ldata);

    if (i>0)
      for (int k=0; k<HWid; k++) ldata[k]=ldata[k]*winhann[k];
    for (int k=HWid; k<Wid; k++) ldata[k]=ldata[k]*winhann[k];

    for (int k=0; k<HWid; k++) tmpdata[k]+=ldata[k];
    memcpy(&dataout[i*HWid], tmpdata, HWid*sizeof(double));
    memcpy(tmpdata, &ldata[HWid], HWid*sizeof(double));
  }
  memcpy(&dataout[Fr*HWid], tmpdata, HWid*sizeof(double));

  max*=maskcoef;

  for (int i=0; i<Wid; i++)
    winhann[i]=winhann[i]*winhamm[i];

  for (int i=0; i<Fr; i++)
  {
    memset(x, 0, sizeof(cdouble)*Wid);
    for (int k=On; k<Off; k++)
    {
      randfi=rand()*M_PI*2/RAND_MAX;
      x[k].x=x[Wid-k].x=max*cos(randfi);
      x[k].y=max*sin(randfi);
      x[Wid-k].y=-x[k].y;
    }

    CIFFTR(x, Order, w, ldata);

    if (i>0)
      for (int k=0; k<HWid; k++)
        ldata[k]=ldata[k]*winhann[k];
    for (int k=HWid; k<Wid; k++)
      ldata[k]=ldata[k]*winhann[k];

    for (int k=0; k<Wid; k++) dataout[i*HWid+k]+=ldata[k];
  }

  memset(&dataout[Fr*HWid+HWid], 0, (Count-Fr*HWid-HWid)*sizeof(double));

  delete[] winhann;
  delete[] winhamm;
  delete[] tmpdata;
  FreeFFTBuffer(ldata);

  return max;
}//FFTMask

//---------------------------------------------------------------------------
/*
  function FindInc: find the element in ordered list data that is closest to value.

  In: data[Count]: a ordered list
      value: the value to locate in the list

  Returns the index of the element in the sorted list which is closest to $value.
*/
int FindInc(double value, double* data, int Count)
{
  if (value>=data[Count-1]) return Count-1;
  if (value<data[0]) return 0;
  int end=InsertInc(value, data, Count, false);
  if (fabs(value-data[end-1])<fabs(value-data[end])) return end-1;
  else return end;
}//FindInc

//---------------------------------------------------------------------------
/*
  function Gaussian: Gaussian function

  In: Vector[Dim]: a vector
      Mean[Dim]: mean of Gaussian function
      Dev[Fim]: diagonal autocorrelation matrix of Gaussian function

  Returns the value of Gaussian function at Vector[].
*/
double Gaussian(int Dim, double* Vector, double* Mean, double* Dev)
{
  double bmt=0, tmp;
  for (int dim=0; dim<Dim; dim++)
  {
    tmp=Vector[dim]-Mean[dim];
    bmt+=tmp*tmp/Dev[dim];
  }
  bmt=-bmt/2;
  tmp=log(Dev[0]);
  for (int dim=1; dim<Dim; dim++) tmp+=log(Dev[dim]);
  bmt-=tmp/2;
  bmt-=Dim*log(M_PI*2)/2;
  bmt=exp(bmt);
  return bmt;
}//Gaussian


//---------------------------------------------------------------------------
/*
  function Hamming: calculates the amplitude spectrum of Hamming window at a given frequency

  In: f: frequency
      T: size of Hamming window

  Returns the amplitude spectrum at specified frequency.
*/
double Hamming(double f, double T)
{
  double omg0=2*M_PI/T;
  double omg=f*2*M_PI;
  cdouble c1, c2, c3;
  cdouble nj(0, -1);
  cdouble pj(0, 1);
  double a=0.54, b=0.46;

  cdouble c=1.0-exp(nj*T*omg);
  double half=0.5;

  if (fabs(omg)<1e-100)
    c1=a*T;
  else
    c1=a*c/(pj*omg);

  if (fabs(omg+omg0)<1e-100)
    c2=b*0.5*T;
  else
    c2=c*b*half/(nj*cdouble(omg+omg0));

  if (fabs(omg-omg0)<1e-100)
    c3=b*0.5*T;
  else
    c3=b*c*half/(nj*cdouble(omg-omg0));

  c=c1+c2+c3;
  return abs(c);
}//Hamming*/

//---------------------------------------------------------------------------
/*
  function HannSq: computes the square norm of Hann window spectrum (window-size-normalized)

  In: x: frequency, in bins
      N: size of Hann window

  Return the square norm.
*/
double HannSq(double x, double N)
{
  double re, im;
  double pim=M_PI*x;
  double pimf=pim/N;
  double pif=M_PI/N;

  double sinpim=sin(pim);
  double sinpimf=sin(pimf);
  double sinpimplus1f=sin(pimf+pif);
  double sinpimminus1f=sin(pimf-pif);

  double spmdivbyspmf, spmdivbyspmpf, spmdivbyspmmf;

  if (sinpimf==0)
    spmdivbyspmf=N, spmdivbyspmpf=spmdivbyspmmf=0;
  else if (sinpimplus1f==0)
    spmdivbyspmpf=-N, spmdivbyspmf=spmdivbyspmmf=0;
  else if (sinpimminus1f==0)
    spmdivbyspmmf=-N, spmdivbyspmf=spmdivbyspmpf=0;
  else
    spmdivbyspmf=sinpim/sinpimf, spmdivbyspmpf=sinpim/sinpimplus1f, spmdivbyspmmf=sinpim/sinpimminus1f;

  re=0.5*spmdivbyspmf-0.25*cos(pif)*(spmdivbyspmpf+spmdivbyspmmf);
  im=0.25*sin(pif)*(-spmdivbyspmpf+spmdivbyspmmf);

  return (re*re+im*im)/(N*N);
}//HannSq

/*
  function Hann: computes the Hann window amplitude spectrum (window-size-normalized).

  In: x: frequency, in bins
      N: size of Hann window

  Return the amplitude spectrum evaluated at x. Maximum 0.5 is reached at x=0. Time 2 to normalize
  maximum to 1.
*/
double Hann(double x, double N)
{
  double pim=M_PI*x;
  double pif=M_PI/N;
  double pimf=pif*x;

  double sinpim=sin(pim);
  double tanpimf=tan(pimf);
  double tanpimplus1f=tan(pimf+pif);
  double tanpimminus1f=tan(pimf-pif);

  double spmdivbyspmf, spmdivbyspmpf, spmdivbyspmmf;

  if (fabs(tanpimf)<1e-10)
    spmdivbyspmf=N, spmdivbyspmpf=spmdivbyspmmf=0;
  else if (fabs(tanpimplus1f)<1e-10)
    spmdivbyspmpf=-N, spmdivbyspmf=spmdivbyspmmf=0;
  else if (fabs(tanpimminus1f)<1e-10)
    spmdivbyspmmf=-N, spmdivbyspmf=spmdivbyspmpf=0;
  else
    spmdivbyspmf=sinpim/tanpimf, spmdivbyspmpf=sinpim/tanpimplus1f, spmdivbyspmmf=sinpim/tanpimminus1f;

  double result=0.5*spmdivbyspmf-0.25*(spmdivbyspmpf+spmdivbyspmmf);

  return result/N;
}//HannC

/*
  function HxPeak2: fine spectral peak detection. This does detection and high-precision LSE estimation
  in one go. However, since in practise most peaks are spurious, LSE estimation is not necessary on
  them. Accordingly, HxPeak2 is deprecated in favour of faster but coarser peak picking methods, such as
  QIFFT, which leaves fine estimation to a later stage of processing.

  In: F, dF, ddF: pointers to functions that compute LSE peak energy for, plus its 1st and 2nd
        derivatives against, a given frequency.
      params: pointer to a data structure (l_hx) hosting input data fed to F, dF, and ddF
      (st, en): frequency range, in bins, to search for peaks in
      epf: convergence detection threshold
  Out: hps[return value]: peak frequencies
       vps[return value]; peak amplitudes

  Returns the number of peaks detected.
*/
int HxPeak2(double*& hps, double*& vhps, double (*F)(double, void*), double (*dF)(double, void*), double(*ddF)(double, void*), void* params, double st, double en, double epf)
{
  struct l_hx {int N; union {double B; struct {int k1; int k2;};}; cdouble* x; double dhxpeak; double hxpeak;} *p=(l_hx *)params;
  int dfshift=int(&((l_hx*)0)->dhxpeak);
  int fshift=int(&((l_hx*)0)->hxpeak);
  double B=p->B;
  int count=0;

  int den=ceil(en), dst=floor(st);
  if (den-dst<3) den++, dst--;
  if (den-dst<3) den++, dst--;
  if (dst<1) dst=1;

  double step=0.5;
  int num=(den-dst)/step+1;
  bool allochps=false, allocvhps=false;
  if (hps==NULL) allochps=true, hps=new double[num];
  if (vhps==NULL) allocvhps=true, vhps=new double[num];

  {
    double* inp=new double[num];
    for (int i=0; i<num; i++)
    {
      double lf=dst+step*i;
      p->k1=ceil(lf-B); if (p->k1<0) p->k1=0;
      p->k2=floor(lf+B); if (p->k2>=p->N/2) p->k2=p->N/2-1;
      inp[i]=F(lf, params);
    }

    for (int i=1; i<num-1; i++)
    {
      if (inp[i]>=inp[i-1] && inp[i]>=inp[i+1])  //inp[i]=F(dst+step*i)
      {
        if (inp[i]==inp[i-1] && inp[i]==inp[i+1]) continue;
        double fa=dst+step*(i-1), fb=dst+step*(i+1);
        double ff=dst+step*i;
        p->k1=ceil(ff-B); if (p->k1<0) p->k1=0;
        p->k2=floor(ff+B); if (p->k2>=p->N/2) p->k2=p->N/2-1;

        double tmp=Newton1dmax(ff, fa, fb, ddF, params, dfshift, fshift, dF, dfshift, epf);

        //although we have selected inp[i] to be a local maximum, different truncation
        //  of local spectrum implies it may not hold as the truncation of inp[i] is
        //  used for recalculating inp[i-1] and inp[i+1] in init_Newton method. In this
        //  case we retry the sub-maximal frequency to see if it becomes a local maximum
        //  when the spectrum is truncated to centre on it.

        if (tmp==-0.5 || tmp==-0.7) //y(fa)<=y(ff)<y(fb) or y(ff)<y(fa)<y(fb)
        {
          tmp=Newton1dmax(fb, ff, 2*fb-ff, ddF, params, dfshift, fshift, dF, dfshift, epf);
          if (tmp==-0.5 || tmp==-0.7) continue;
          /*
          double ff2=(ff+fb)/2;
          p->k1=ceil(ff2-B); if (p->k1<0) p->k1=0;
          p->k2=floor(ff2+B); if (p->k2>=p->N/2) p->k2=p->N/2-1;
          tmp=Newton1dmax(ff2, ff, fb, ddF, params, dfshift, fshift, dF, dfshift, epf);
          p->k1=ceil(ff-B); if (p->k1<0) p->k1=0;
          p->k2=floor(ff+B); if (p->k2>=p->N/2) p->k2=p->N/2-1;    */
        }
        else if (tmp==-0.6 || tmp==-0.8) //y(fb)<=y(ff)<y(fa)
        {
          tmp=Newton1dmax(fa, 2*fa-ff, ff, ddF, params, dfshift, fshift, dF, dfshift, epf);
          if (tmp==-0.6 || tmp==-0.8) continue;
        }
        if (tmp<0 /*tmp==-0.5 || tmp==-0.6 || tmp==-1 || tmp==-2 || tmp==-3*/)
        {
          Search1Dmax(ff, params, F, dst+step*(i-1), dst+step*(i+1), &vhps[count], epf);
        }
        else
        {
          vhps[count]=p->hxpeak;
        }
        if (ff>=st && ff<=en && ff>dst+step*(i-0.99) && ff<dst+step*(i+0.99))
        {
//          if (count==0 || fabs(tmp-hps[count-1])>0.1)
//          {
            hps[count]=ff;
            count++;
//          }
        }
      }
    }
    delete[] inp;
  }

  if (allochps) hps=(double*)realloc(hps, sizeof(double)*count);
  if (allocvhps) vhps=(double*)realloc(vhps, sizeof(double)*count);
  return count;
}//HxPeak2

//---------------------------------------------------------------------------
/*
  function InsertDec: inserts value into sorted decreasing list

  In: data[Count]: a sorted decreasing list.
      value: the value to be added
  Out: data[Count]: the list with $value inserted if the latter is larger than its last entry, in which
        case the original last entry is discarded.

  Returns the index where $value is located in data[], or -1 if $value is smaller than or equal to
  data[Count-1].
*/
int InsertDec(int value, int* data, int Count)
{
  if (Count<=0) return -1;
  if (value<=data[Count-1]) return -1;
  if (value>data[0])
  {
    memmove(&data[1], &data[0], sizeof(int)*(Count-1));
    data[0]=value;
    return 0;
  }

  //now Count>=2
  int head=0, end=Count-1, mid;

  //D(head)>=value>D(end)
  while (end-head>1)
  {
    mid=(head+end)/2;
    if (value<=data[mid]) head=mid;
    else end=mid;
  }

  //D(head=end-1)>=value>D(end)
  memmove(&data[end+1], &data[end], sizeof(int)*(Count-end-1));
  data[end]=value;
  return end;
}//InsertDec
//the double version
int InsertDec(double value, double* data, int Count)
{
  if (Count<=0) return -1;
  if (value<=data[Count-1]) return -1;
  if (value>data[0])
  {
    memmove(&data[1], &data[0], sizeof(double)*(Count-1));
    data[0]=value;
    return 0;
  }

  //now Count>=2
  int head=0, end=Count-1, mid;

  //D(head)>=value>D(end)
  while (end-head>1)
  {
    mid=(head+end)/2;
    if (value<=data[mid]) head=mid;
    else end=mid;
  }

  //D(head=end-1)>=value>D(end)
  memmove(&data[end+1], &data[end], sizeof(double)*(Count-end-1));
  data[end]=value;
  return end;
}//InsertDec

/*
  function InsertDec: inserts value and attached integer into sorted decreasing list

  In: data[Count]: a sorted decreasing list
      indices[Count]: a list of integers attached to entries of data[]
      value, index: the value to be added and its attached integer
  Out: data[Count], indices[Count]: the lists with $value and $index inserted if $value is larger than
        the last entry of data[], in which case the original last entries are discarded.

  Returns the index where $value is located in data[], or -1 if $value is smaller than or equal to
  data[Count-1].
*/
int InsertDec(double value, int index, double* data, int* indices, int Count)
{
  if (Count<=0) return -1;
  if (value<=data[Count-1]) return -1;
  if (value>data[0])
  {
    memmove(&data[1], data, sizeof(double)*(Count-1));
    memmove(&indices[1], indices, sizeof(int)*(Count-1));
    data[0]=value, indices[0]=index;
    return 0;
  }

  //now Count>=2
  int head=0, end=Count-1, mid;

  //D(head)>=value>D(end)
  while (end-head>1)
  {
    mid=(head+end)/2;
    if (value<=data[mid]) head=mid;
    else end=mid;
  }

  //D(head=end-1)>=value>D(end)
  memmove(&data[end+1], &data[end], sizeof(double)*(Count-end-1));
  memmove(&indices[end+1], &indices[end], sizeof(int)*(Count-end-1));
  data[end]=value, indices[end]=index;
  return end;
}//InsertDec

/*
  InsertInc: inserts value into sorted increasing list.

  In: data[Count]: a sorted increasing list.
      Capacity: maximal size of data[]
      value: the value to be added
      Compare: pointer to function that compare two values
  Out: data[Count]: the list with $value inserted. If the original list is full (Count=Capacity) then
        either $value, or the last entry of data[], whichever is larger, is discarded.

  Returns the index where $value is located in data[], or -1 if it is not inserted, which happens if
  Count=Capacity and $value is larger than or equal to the last entry in data[Capacity].
*/
int InsertInc(void* value, void** data, int Count, int Capacity, int (*Compare)(void*, void*))
{
  if (Capacity<=0) return -1;
  if (Count>Capacity) Count=Capacity;

//Compare(A,B)<0 if A<B, =0 if A=B, >0 if A>B
  int PosToInsert;
  if (Count==0) PosToInsert=0;
  else if (Compare(value, data[Count-1])>0) PosToInsert=Count;
  else if (Compare(value, data[0])<0) PosToInsert=0;
  else
  {
    //now Count>=2
    int head=0, end=Count-1, mid;

    //D(head)<=value<D(end)
    while (end-head>1)
    {
      mid=(head+end)/2;
      if (Compare(value, data[mid])>=0) head=mid;
      else end=mid;
    }
    //D(head=end-1)<=value<D(end)
    PosToInsert=end;
  }

  if (Count<Capacity)
  {
    memmove(&data[PosToInsert+1], &data[PosToInsert], sizeof(void*)*(Count-PosToInsert));
    data[PosToInsert]=value;
  }
  else //Count==Capacity
  {
    if (PosToInsert>=Capacity) return -1;
    memmove(&data[PosToInsert+1], &data[PosToInsert], sizeof(void*)*(Count-PosToInsert-1));
    data[PosToInsert]=value;
  }
  return PosToInsert;
}//InsertInc

/*
  function InsertInc: inserts value into sorted increasing list

  In: data[Count]: a sorted increasing list.
      value: the value to be added
      doinsert: specifies whether the actually insertion is to be performed
  Out: data[Count]: the list with $value inserted if the latter is smaller than its last entry, in which
        case the original last entry of data[] is discarded.

  Returns the index where $value is located in data[], or -1 if value is larger than or equal to
  data[Count-1].
*/
int InsertInc(double value, double* data, int Count, bool doinsert)
{
  if (Count<=0) return -1;
  if (value>=data[Count-1]) return -1;
  if (value<data[0])
  {
    memmove(&data[1], &data[0], sizeof(double)*(Count-1));
    if (doinsert) data[0]=value;
    return 0;
  }

  //now Count>=2
  int head=0, end=Count-1, mid;

  //D(head)<=value<D(end)
  while (end-head>1)
  {
    mid=(head+end)/2;
    if (value>=data[mid]) head=mid;
    else end=mid;
  }

  //D(head=end-1)<=value<D(end)
  if (doinsert)
  {
    memmove(&data[end+1], &data[end], sizeof(double)*(Count-end-1));
    data[end]=value;
  }
  return end;
}//InsertInc
//version where data[] is int.
int InsertInc(double value, int* data, int Count, bool doinsert)
{
  if (Count<=0) return -1;
  if (value>=data[Count-1]) return -1;
  if (value<data[0])
  {
    memmove(&data[1], &data[0], sizeof(int)*(Count-1));
    if (doinsert) data[0]=value;
    return 0;
  }

  //now Count>=2
  int head=0, end=Count-1, mid;

  //D(head)<=value<D(end)
  while (end-head>1)
  {
    mid=(head+end)/2;
    if (value>=data[mid]) head=mid;
    else end=mid;
  }

  //D(head=end-1)<=value<D(end)
  if (doinsert)
  {
    memmove(&data[end+1], &data[end], sizeof(int)*(Count-end-1));
    data[end]=value;
  }
  return end;
}//InsertInc

/*
  function InsertInc: inserts value and attached integer into sorted increasing list

  In: data[Count]: a sorted increasing list
      indices[Count]: a list of integers attached to entries of data[]
      value, index: the value to be added and its attached integer
  Out: data[Count], indices[Count]: the lists with $value and $index inserted if $value is smaller than
        the last entry of data[], in which case the original last entries are discarded.

  Returns the index where $value is located in data[], or -1 if $value is larger than or equal to
  data[Count-1].
*/
int InsertInc(double value, int index, double* data, int* indices, int Count)
{
  if (Count<=0) return -1;
  if (value>=data[Count-1]) return -1;
  if (value<data[0])
  {
    memmove(&data[1], data, sizeof(double)*(Count-1));
    memmove(&indices[1], indices, sizeof(int)*(Count-1));
    data[0]=value, indices[0]=index;
    return 0;
  }

  //now Count>=2
  int head=0, end=Count-1, mid;

  //D(head)>=value>D(end)
  while (end-head>1)
  {
    mid=(head+end)/2;
    if (value>=data[mid]) head=mid;
    else end=mid;
  }

  //D(head=end-1)>=value>D(end)
  memmove(&data[end+1], &data[end], sizeof(double)*(Count-end-1));
  memmove(&indices[end+1], &indices[end], sizeof(int)*(Count-end-1));
  data[end]=value, indices[end]=index;
  return end;
}//InsertInc
//version where indices[] is double-precision floating point.
int InsertInc(double value, double index, double* data, double* indices, int Count)
{
  if (Count<=0) return -1;
  if (value>=data[Count-1]) return -1;
  if (value<data[0])
  {
    memmove(&data[1], data, sizeof(double)*(Count-1));
    memmove(&indices[1], indices, sizeof(double)*(Count-1));
    data[0]=value, indices[0]=index;
    return 0;
  }

  //now Count>=2
  int head=0, end=Count-1, mid;

  //D(head)>=value>D(end)
  while (end-head>1)
  {
    mid=(head+end)/2;
    if (value>=data[mid]) head=mid;
    else end=mid;
  }

  //D(head=end-1)>=value>D(end)
  memmove(&data[end+1], &data[end], sizeof(double)*(Count-end-1));
  memmove(&indices[end+1], &indices[end], sizeof(double)*(Count-end-1));
  data[end]=value, indices[end]=index;
  return end;
}//InsertInc

/*
  function InsertIncApp: inserts value into flexible-length sorted increasing list

  In: data[Count]: a sorted increasing list.
      value: the value to be added
  Out: data[Count+1]: the list with $value inserted.

  Returns the index where $value is located in data[], or -1 if Count<0. data[] must have Count+1
  storage units on calling.
*/
int InsertIncApp(double value, double* data, int Count)
{
  if (Count<0) return -1;
  if (Count==0){data[0]=value; return 0;}
  if (value>=data[Count-1]){data[Count]=value; return Count;}
  if (value<data[0])
  {
    memmove(&data[1], &data[0], sizeof(double)*Count);
    data[0]=value;
    return 0;
  }

  //now Count>=2
  int head=0, end=Count-1, mid;

  //D(head)<=value<D(end)
  while (end-head>1)
  {
    mid=(head+end)/2;
    if (value>=data[mid]) head=mid;
    else end=mid;
  }

  //D(head=end-1)<=value<D(end)
  memmove(&data[end+1], &data[end], sizeof(double)*(Count-end));
  data[end]=value;

  return end;
}//InsertIncApp

//---------------------------------------------------------------------------
/*
  function InstantFreq; calculates instantaneous frequency from spectrum, evaluated at bin k
  
  In: x[hwid]: spectrum with scale 2hwid
      k: reference frequency, in bins
      mode: must be 1.
  
  Returns an instantaneous frequency near bin k.
*/
double InstantFreq(int k, int hwid, cdouble* x, int mode)
{
  double result;
  switch(mode)
  {
    //mode 1: the phase vocoder method, based on J. Brown, where the spectrogram
    //  MUST be calculated using rectangular window
    case 1:
    {
      if (k<1) k=1;
      if (k>hwid-2) k=hwid-2;
      double hr=0.5*(x[k].x-0.5*(x[k+1].x+x[k-1].x)), hi=0.5*(x[k].y-0.5*(x[k+1].y+x[k-1].y));
      double ph0=Atan2(hi, hr);
      double c=cos(M_PI/hwid), s=sin(M_PI/hwid);
      hr=0.5*(x[k].x-0.5*(x[k+1].x*c-x[k+1].y*s+x[k-1].x*c+x[k-1].y*s));
      hi=0.5*(x[k].y-0.5*(x[k+1].y*c+x[k+1].x*s+x[k-1].y*c-x[k-1].x*s));
      double ph1=Atan2(hi, hr);
      result=(ph1-ph0)/(2*M_PI);
      if (result<-0.5) result+=1;
      if (result>0.5) result-=1;
      result+=k*0.5/hwid;
      break;
    }
    case 2:
      break;
  }
  return result;
}//InstantFreq

/*
  function InstantFreq; calculates "frequency spectrum", a sequence of frequencies evaluated at DFT bins
  
  In: x[hwid]: spectrum with scale 2hwid
      mode: must be 1.
  Out: freqspec[hwid]: the frequency spectrum
    
  No return value.
*/
void InstantFreq(double* freqspec, int hwid, cdouble* x, int mode)
{
  for (int i=0; i<hwid; i++)
    freqspec[i]=InstantFreq(i, hwid, x, mode);
}//InstantFreq

//---------------------------------------------------------------------------
/*
  function IntToDouble: copy content of integer array to double array

  In: in: pointer to integer array
      BytesPerSample: number of bytes each integer takes
      Count: size of integer array, in integers
  Out: vector out[Count].

  No return value.

  This version is currently commented out in favour of the version implemented in QuickSpec.cpp which
  supports 24-bit integers.
*//*
void IntToDouble(double* out, void* in, int BytesPerSample, int Count)
{
  if (BytesPerSample==1){unsigned char* in8=(unsigned char*)in; for (int k=0; k<Count; k++) *(out++)=*(in8++)-128.0;}
  else {__int16* in16=(__int16*)in; for (int k=0; k<Count; k++) *(out++)=*(in16++);}
}//IntToDouble*/

//---------------------------------------------------------------------------
/*
  function IPHannC: inner product with Hann window spectrum

  In: x[N]: spectrum
      f: reference frequency
      K1, K2: spectral truncation bounds

  Returns the absolute value of the inner product of x[K1:K2] with the corresponding band of the
  spectrum of a sinusoid at frequency f.
*/
double IPHannC(double f, cdouble* x, int N, int K1, int K2)
{
  int M; double c[4], iH2;
  windowspec(wtHann, N, &M, c, &iH2);
  return abs(IPWindowC(f, x, N, M, c, iH2, K1, K2));
}//IPHannC


//---------------------------------------------------------------------------
/*
  function lse: linear regression y=ax+b

  In: x[Count], y[Count]: input points
  Out: a, b: LSE estimation of coefficients in y=ax+b

  No return value.
*/
void lse(double* x, double* y, int Count, double& a, double& b)
{
  double sx=0, sy=0, sxx=0, sxy=0;
  for (int i=0; i<Count; i++)
  {
    sx+=x[i];
    sy+=y[i];
    sxx+=x[i]*x[i];
    sxy+=x[i]*y[i];
  }
  b=(sxx*sy-sx*sxy)/(Count*sxx-sx*sx);
  a=(sy-Count*b)/sx;
}//lse

//--------------------------------------------------------------------------
/*
  memdoubleadd: vector addition

  In: dest[count], source[count]: addends
  Out: dest[count]: sum

  No return value.
*/
void memdoubleadd(double* dest, double* source, int count)
{
  for (int i=0; i<count; i++){*dest=*dest+*source; dest++; source++;}
}//memdoubleadd

//--------------------------------------------------------------------------
/*
  function Mel: converts frequency in Hz to frequency in mel.

  In: f: frequency, in Hz

  Returns the frequency measured on mel scale.
*/
double Mel(double f)
{
  return 1127.01048*log(1+f/700);
}//Mel

/*
  function InvMel: converts frequency in mel to frequency in Hz.

  In: f: frequency, in mel.

  Returns the frequency in Hz.
*/
double InvMel(double mel)
{
  return 700*(exp(mel/1127.01048)-1);
}//InvMel

/*
  function MFCC: calculates MFCC.

  In: Data[FrameWidth]: data
      NumBands: number of frequency bands on mel scale
      Bands[3*NumBands]: mel frequency bands, comes as $NumBands triples, each containing the lower,
        middle and high frequencies, in bins, of one band, from which a weighting window is created to
        weight individual bins.
      Ceps_Order: number of MFC coefficients (i.e. DCT coefficients)
      W, X: FFT buffers
  Out: C[Ceps_Order]: MFCC
       Amps[NumBands]: log spectrum on MF bands

  No return value. Use MFCCPrepareBands() to retrieve Bands[].
*/
void MFCC(int FrameWidth, int NumBands, int Ceps_Order, double* Data, double* Bands, double* C, double* Amps, cdouble* W, cdouble* X)
{
  double tmp, b2s, b2c, b2e;

  RFFTC(Data, 0, 0, log2(FrameWidth), W, X, 0);
  for (int i=0; i<=FrameWidth/2; i++) Amps[i]=X[i].x*X[i].x+X[i].y*X[i].y;
  
  for (int i=0; i<NumBands; i++)
  {
    tmp=0;
    b2s=Bands[3*i], b2c=Bands[3*i+1], b2e=Bands[3*i+2];

    for (int j=ceil(b2s); j<ceil(b2c); j++)
      tmp+=Amps[j]*(j-b2s)/(b2c-b2s);
    for (int j=ceil(b2c); j<b2e; j++)
      tmp+=Amps[j]*(b2e-j)/(b2e-b2c);

    if (tmp<3.7200759760208359629596958038631e-44)
      Amps[i]=-100;
    else
      Amps[i]=log(tmp);
  }

  for (int i=0; i<Ceps_Order; i++)
  {
    tmp=Amps[0]*cos(M_PI*(i+1)/2/NumBands);
    for (int j=1; j<NumBands; j++)
      tmp+=Amps[j]*cos(M_PI*(i+0.5)*(j+0.5)/NumBands);
    C[i]=tmp;
  }
}//MFCC

/*
  function MFCCPrepareBands: returns a array of OVERLAPPING bands given in triples, whose 1st and 3rd
  entries are the start and end of a band, in bins, and the 2nd is a middle frequency.

  In: SamplesPerSec: sampling rate
      NumberOfBins: FFT size
      NumberOfBands: number of mel-frequency bands

  Returns pointer to the array of triples.
*/
double* MFCCPrepareBands(int NumberOfBands, int SamplesPerSec, int NumberOfBins)
{
  double* Bands=new double[NumberOfBands*3];
  double naqfreq=SamplesPerSec/2.0; //naqvist freq
  double binwid=SamplesPerSec*1.0/NumberOfBins;
  double naqmel=Mel(naqfreq);
  double b=naqmel/(NumberOfBands+1);

  Bands[0]=0;
  Bands[1]=InvMel(b)/binwid;
  Bands[2]=InvMel(b*2)/binwid;
  for (int i=1; i<NumberOfBands; i++)
  {
    Bands[3*i]=Bands[3*i-2];
    Bands[3*i+1]=Bands[3*i-1];
    Bands[3*i+2]=InvMel(b*(i+2))/binwid;
  }
  return Bands;
}//MFCCPrepareBands

//---------------------------------------------------------------------------
/*
  function Multi: vector-constant multiplication

  In: data[count]: a vector
      mul: a constant
  Out: data[count]: their product

  No return value.
*/
void Multi(double* data, int count, double mul)
{
  for (int i=0; i<count; i++){*data=*data*mul; data++;}
}//Multi

/*
  function Multi: vector-constant multiplication

  In: in[count]: a vector
      mul: a constant
  Out: out[count]: their product

  No return value.
*/
void Multi(double* out, double* in, int count, double mul)
{
  for (int i=0; i<count; i++) *(out++)=*(in++)*mul;
}//Multi

/*
  function Multi: vector-constant multiply-addition

  In: in[count], adder[count]: vectors
      mul: a constant
  Out: out[count]: in[]+adder[]*mul

  No return value.
*/
void MultiAdd(double* out, double* in, double* adder, int count, double mul)
{
  for (int i=0; i<count; i++) *(out++)=*(in++)+*(adder++)*mul;
}//MultiAdd

//---------------------------------------------------------------------------
/*
  function NearestPeak: finds a peak value in an array that is nearest to a given index

  In: data[count]: an array
      anindex: an index

  Returns the index to a peak of data[] that is closest to anindex. In case of two cloest indices,
  returns the index to the higher peak of the two.
*/
int NearestPeak(double* data, int count, int anindex)
{
  int upind=anindex, downind=anindex;
  if (anindex<1) anindex=1;
  if (anindex>count-2) anindex=count-2;

  if (data[anindex]>data[anindex-1] && data[anindex]>data[anindex+1]) return anindex;

  if (data[anindex]<data[anindex-1])
    while (downind>0 && data[downind-1]>data[downind]) downind--;
  if (data[anindex]<data[anindex+1])
    while (upind<count-1 && data[upind]<data[upind+1]) upind++;

  if (upind==anindex) return downind;
  if (downind==anindex) return upind;

  if (anindex-downind<upind-anindex) return downind;
  else if (anindex-downind>upind-anindex) return upind;
  else if (data[upind]<data[downind]) return downind;
  else return upind;
}//NearestPeak

//---------------------------------------------------------------------------
/*
  function NegativeExp: fits the curve y=1-x^lmd.

  In: x[Count], y[Count]: sample points to fit, x[0]=0, x[Count-1]=1, y[0]=1, y[Count-1]=0
  Out: lmd: coefficient of y=1-x^lmd.

  Returns rms fitting error.
*/
double NegativeExp(double* x, double* y, int Count, double& lmd, int sample, double step, double eps, int maxiter)
{
  lmd=0;
  for (int i=1; i<Count-1; i++)
  {
    if (y[i]<1)
      lmd+=log(1-y[i])/log(x[i]);
    else
      lmd+=-50/log(x[i]);
  }
  lmd/=(Count-2);

//lmd has been initialized
//coming up will be the recursive calculation of lmd by lgg

  int iter=0;
  double laste, lastdel, e=0, del=0, tmp;
  do
  {
    iter++;
    laste=e;
    lastdel=del;
    e=0, del=0;
    for (int i=1; i<Count-1; i+=sample)
    {
      tmp=pow(x[i], lmd);
      e=e+(y[i]+tmp-1)*(y[i]+tmp-1);
      del=del+(y[i]+tmp-1)*tmp*log(x[i]);
    }
    if (laste && e>laste) lmd+=step*lastdel, step/=2;
    else lmd+=-step*sample*del;
  }
  while (e && iter<=maxiter && (!laste || fabs(laste-e)/e>eps));
  return sqrt(e/Count);
}//NegativeExp

//---------------------------------------------------------------------------
/*
  function: NL: noise level, calculated on 5% of total frames with least energy

  In: data[Count]:
      Wid: window width for power level estimation

  Returns noise level, in rms.
*/
double NL(double* data, int Count, int Wid)
{
  int Fr=Count/Wid;
  int Num=Fr/20+1;
  double* ene=new double[Num], tmp;
  for (int i=0; i<Num; i++) ene[i]=1e30;
  for (int i=0; i<Fr; i++)
  {
    tmp=DCPower(&data[i*Wid], Wid, 0);
    InsertInc(tmp, ene, Num);
  }
  double result=Avg(ene, Num, 0);
  delete[] ene;
  result=sqrt(result);
  return result;
}//NL

//---------------------------------------------------------------------------
/*
  function Normalize: normalizes data to [-Maxi, Maxi], without zero shift

  In: data[Count]: data to be normalized
      Maxi: destination maximal absolute value
  Out: data[Count]: normalized data

  Returns the original maximal absolute value.
*/
double Normalize(double* data, int Count, double Maxi)
{
  double max=0;
  double* ldata=data;
  for (int i=0; i<Count; i++)
  {
    if (*ldata>max) max=*ldata;
    else if (-*ldata>max) max=-*ldata;
    ldata++;
  }
  if (max>0)
  {
    Maxi=Maxi/max;
    for (int i=0; i<Count; i++) *(data++)*=Maxi;
  }
  return max;
}//Normalize

/*
  function Normalize2: normalizes data to a specified Euclidian norm

  In: data[Count]: data to normalize
      Norm: destination Euclidian norm
  Out: data[Count]: normalized data.

  Returns the original Euclidian norm.
*/
double Normalize2(double* data, int Count, double Norm)
{
  double norm=0;
  for (int i=0; i<Count; i++) norm+=data[i]*data[i];
  norm=sqrt(norm);
  double mul=norm/Norm;
  if (mul!=0) for (int i=0; i<Count; i++) data[i]/=mul;
  return norm;
}//Normalize2

//---------------------------------------------------------------------------
/*
  function PhaseSpan: computes the unwrapped phase variation across the Nyquist range

  In: data[Count]: time-domain data
      aparams: a fftparams structure

  Returns the difference between unwrapped phase angles at 0 and Nyquist frequency.
*/
double PhaseSpan(double* data, int Count, void* aparams)
{
  int Pad=1;
  fftparams* params=(fftparams*)aparams;
  double* Arg=new double[Count*Pad];
  AllocateFFTBuffer(Count*Pad, Amp, w, x);
  memset(Amp, 0, sizeof(double)*Count*Pad);
  memcpy(&Amp[Count*(Pad-1)/2], data, sizeof(double)*Count);
  ApplyWindow(Amp, Amp, params->Amp, Count);
  RFFTC(Amp, Amp, Arg, log2(Count*Pad), w, x, 0);

  SmoothPhase(Arg, Count*Pad/2+1);
  double result=Arg[Count*Pad/2]-Arg[0];
  delete[] Arg;
  FreeFFTBuffer(Amp);
  return result;
}//PhaseSpan

//---------------------------------------------------------------------------
/*
  function PolyFit: least square polynomial fitting y=sum(i){a[i]*x^i}

  In: x[N], y[N]: sample points
      P: order of polynomial, P<N
  Out: a[P+1]: coefficients of polynomial

  No return value.
*/
void PolyFit(int P, double* a, int N, double* x, double* y)
{
  Alloc2(P+1, P+1, aa);
  double ai0, bi, *bb=new double[P+1], *s=new double[N], *r=new double[N];
  aa[0][0]=N; bi=0; for (int i=0; i<N; i++) s[i]=1, r[i]=y[i], bi+=y[i]; bb[0]=bi;

  for (int i=1; i<=P; i++)
  {
    ai0=bi=0; for (int j=0; j<N; j++) {s[j]*=x[j], r[j]*=x[j]; ai0+=s[j], bi+=r[j];}
    for (int j=0; j<=i; j++) aa[i-j][j]=ai0; bb[i]=bi;
  }
  for (int i=P+1; i<=2*P; i++)
  {
    ai0=0; for (int j=0; j<N; j++) {s[j]*=x[j]; ai0+=s[j];}
    for (int j=i-P; j<=P; j++) aa[i-j][j]=ai0;
  }
  GESCP(P+1, a, aa, bb);
  DeAlloc2(aa); delete[] bb; delete[] s; delete[] r;
}//PolyFit

//---------------------------------------------------------------------------
/*
  function Pow: vector power function

  In: data[Count]: a vector
      ex: expontnet
  Out: data[Count]: point-wise $ex-th power of data[]

  No return value.
*/
void Pow(double* data, int Count, double ex)
{
  for (int i=0; i<Count; i++)
    data[i]=pow(data[i], ex);
}//Power

//---------------------------------------------------------------------------
/*
  Rectify: semi-wave rectification

  In: data[Count]: data to rectify
      th: rectification threshold: values below th are rectified to th
  Out: data[Count]: recitified data

  Returns number of preserved (i.e. not rectified) samples.
*/
int Rectify(double* data, int Count, double th)
{
  int Result=0;
  for (int i=0; i<Count; i++)
  {
    if (data[i]<=th) data[i]=th;
    else Result++;
  }
  return Result;
}//Rectify

//---------------------------------------------------------------------------
/*
  function Res: minimum absolute residue.

  In: in: a number
      mod: modulus

  Returns the minimal absolute residue of $in devided by $mod.
*/
double Res(double in, double mod)
{
  int i=in/mod;
  in=in-i*mod;
  if (in>mod/2) in-=mod;
  if (in<-mod/2) in+=mod;
  return in;
}//Res

//---------------------------------------------------------------------------
/*
  function Romberg: Romberg algorithm for numerical integration

  In: f: function to integrate
      params: extra argument for calling f
      a, b: integration boundaries
      n: depth of sampling

  Returns the integral of f(*, params) over [a, b].
*/
double Romberg(int n, double(*f)(double, void*), double a, double b, void* params)
{
  int np=1;
  double* r1=new double[n+1];.
  double* r2=new double[n+1];
  double h=b-a, *swp;
  r1[1]=h*(f(a, params)+f(b, params))/2;
  for (int i=2; i<=n; i++)
  {
    double akh=a+0.5*h; r2[1]=f(akh, params);
    for (int k=2; k<=np; k++) {akh+=h; r2[1]+=f(akh, params);} //akh=a+(k-0.5)h
    r2[1]=0.5*(r1[1]+h*r2[1]);
    double fj=4;
    for (int j=2; j<=i; j++) {r2[j]=(fj*r2[j-1]-r1[j-1])/(fj-1); fj*=4;}  //fj=4^(j-1)
    h/=2; np*=2;
    swp=r1; r1=r2; r2=swp;
  }
  h=r1[n];
  delete[] r1;
  delete[] r2;
  return h;
}//Romberg

/*
  function Romberg: Romberg algorithm for numerical integration, may return before specified depth on
  convergence.

  In: f: function to integrate
      params: extra argument for calling f
      a, b: integration boundaries
      n: depth of sampling
      ep: convergence test threshold

  Returns the integral of f(*, params) over [a, b].
*/
double Romberg(double(*f)(double, void*), double a, double b, void* params, int n, double ep)
{
  int i, np=1;
  double* r1=new double[n+1];
  double* r2=new double[n+1];
  double h=b-a, *swp;
  r1[1]=h*(f(a, params)+f(b, params))/2;
  bool mep=false;
  for (i=2; i<=n; i++)
  {
    double akh=a+0.5*h; r2[1]=f(akh, params);
    for (int k=2; k<=np; k++) {akh+=h; r2[1]+=f(akh, params);} //akh=a+(k-0.5)h
    r2[1]=0.5*(r1[1]+h*r2[1]);
    double fj=4;
    for (int j=2; j<=i; j++) {r2[j]=(fj*r2[j-1]-r1[j-1])/(fj-1); fj*=4;}  //fj=4^(j-1)

    if (fabs(r2[i]-r1[i-1])<ep)
    {
      if (mep) break;
      else mep=true;
    }
    else mep=false;

    h/=2; np*=2;
    swp=r1; r1=r2; r2=swp;
  }
  if (i<=n) h=r2[i];
  else h=r1[n];
  delete[] r1;
  delete[] r2;
  return h;
}//Romberg

//---------------------------------------------------------------------------
//analog and digital sinc functions

//sinca(0)=1, sincd(0)=N, sinca(1)=sincd(1)=0.
/*
  function sinca: analog sinc function.

  In: x: frequency

  Returns sinc(x)=sin(pi*x)/(pi*x), sinca(0)=1, sinca(1)=0
*/
double sinca(double x)
{
  if (x==0) return 1;
  return sin(M_PI*x)/(M_PI*x);
}//sinca

/*
  function sincd_unn: unnormalized discrete sinc function

  In: x: frequency
      N: scale (window size, DFT size)

  Returns sinc(x)=sin(pi*x)/sin(pi*x/N), sincd(0)=N, sincd(1)=0.
*/
double sincd_unn(double x, int N)
{
  if (x==0) return N;
  return sin(M_PI*x)/sin(M_PI*x/N);
}//sincd

//---------------------------------------------------------------------------
/*
  SmoothPhase: phase unwrapping on module mpi*PI, 2PI by default

  In: Arg[Count]: phase angles to unwrap
      mpi: unwrapping modulus, in pi's
  Out: Arg[Count]: unwrapped phase

  Returns the amount of unwrap, in pi's, of the last phase angle
*/
double SmoothPhase(double* Arg, int Count, int mpi)
{
  double m2pi=mpi*M_PI;
  for (int i=1; i<Count-1; i++)
    Arg[i]=Arg[i-1]+Res(Arg[i]-Arg[i-1], m2pi);
  double tmp=Res(Arg[Count-1]-Arg[Count-2], m2pi);
  double result=(Arg[Count-1]-Arg[Count-2]-tmp)/m2pi;
  Arg[Count-1]=Arg[Count-2]+tmp;

  return result;
}//SmoothPhase

//---------------------------------------------------------------------------
//the stiff string partial frequency model f[m]=mf[1]*sqrt(1+B(m*m-1)).

/*
  StiffB: computes stiffness coefficient from fundamental and another partial frequency based on the
  stiff string partial frequency model f[m]=mf[1]*sqrt(1+B(m*m-1)).

  In: f0: fundamental frequency
      m: 1-based partial index
      fm: frequency of partial m

  Returns stiffness coefficient B.
*/
double StiffB(double f0, double fm, int m)
{
  double f2=fm/m/f0;
  return (f2*f2-1)/(m*m-1);
}//StiffB

//StiffF: partial frequency of a stiff string
/*
  StiffFm: computes a partial frequency from fundamental frequency and partial index based on the stiff
  string partial frequency model f[m]=mf[1]*sqrt(1+B(m*m-1)).

  In: f0: fundamental frequency
      m: 1-based partial index
      B: stiffness coefficient

  Returns frequency of the m-th partial.
*/
double StiffFm(double f0, int m, double B)
{
  return m*f0*sqrt(1+B*(m*m-1));
}//StiffFm

/*
  StiffF0: computes fundamental frequency from another partial frequency and stiffness coefficient based
  on the stiff string partial frequency model f[m]=mf[1]*sqrt(1+B(m*m-1)).

  In: m: 1-based partial index
      fm: frequency of partial m
      B: stiffness coefficient

  Returns the fundamental frequency.
*/
double StiffF0(double fm, int m, double B)
{
  return fm/m/sqrt(1+B*(m*m-1));
}//StiffF0

/*
  StiffM: computes 1-based partial index from partial frequency, fundamental frequency and stiffness
  coefficient based on the stiff string partial frequency model f[m]=mf[1]*sqrt(1+B(m*m-1)).

  In: f0: fundamental freqency
      fm: a partial frequency
      B: stiffness coefficient

  Returns the 1-based partial index which will generate the specified partial frequency from the model
  which, however, does not have to be an integer in this function.
*/
double StiffM(double f0, double fm, double B)
{
  if (B<1e-14) return fm/f0;
  double b1=B-1, ff=fm/f0;
  double delta=b1*b1+4*B*ff*ff;
  if (delta<0)
    return sqrt(b1/2/B);
  else
    return sqrt((b1+sqrt(delta))/2/B);
}//StiffMd

//---------------------------------------------------------------------------
/*
  TFFilter: time-frequency filtering with Hann-windowed overlap-add.

  In: data[Count]: input data
      Spans: time-frequency spans
      wt, windp: type and extra parameter of FFT window
      Sps: sampling rate
      TOffst: optional offset applied to all time values in Spans, set to Spans timing of of data[0].
      Pass: set to pass T-F content covered by Spans, clear to stop T-F content covered by Spans
  Out: dataout[Count]: filtered data

  No return value. Identical data and dataout allowed.
*/
void TFFilter(double* data, double* dataout, int Count, int Wid, TTFSpans* Spans, bool Pass, WindowType wt, double windp, int Sps, int TOffst)
{
  int HWid=Wid/2;
  int Fr=Count/HWid-1;
  int Order=log2(Wid);

  int** lspan=new int*[Fr];
  double* lxspan=new double[Fr];

  lspan[0]=new int[Fr*Wid];
  for (int i=1; i<Fr; i++)
    lspan[i]=&lspan[0][i*Wid];

  //fill local filter span table
  if (Pass)
    memset(lspan[0], 0, sizeof(int)*Fr*Wid);
  else
    for (int i=0; i<Fr; i++)
      for (int j=0; j<Wid; j++)
        lspan[i][j]=1;

  for (int i=0; i<Spans->Count; i++)
  {
    int lx1, lx2, ly1, ly2;
    lx1=(Spans->Items[i].T1-TOffst)/HWid-1;
    lx2=(Spans->Items[i].T2-1-TOffst)/HWid;
    ly1=Spans->Items[i].F1*2/Sps*HWid+0.001;
    ly2=Spans->Items[i].F2*2/Sps*HWid+1;
    if (lx1<0) lx1=0;
    if (lx2>=Fr) lx2=Fr-1;
    if (ly1<0) ly1=0;
    if (ly1>HWid) ly1=HWid;
    if (Pass)
      for (int x=lx1; x<=lx2; x++)
        for (int y=ly1; y<=ly2; y++)
          lspan[x][y]=1;
    else
      for (int x=lx1; x<=lx2; x++)
        for (int y=ly1; y<=ly2; y++)
          lspan[x][y]=0;
  }
  for (int i=0; i<Fr; i++)
  {
    lxspan[i]=0;
    for (int j=0; j<=HWid; j++)
    {
      if (lspan[i][j])
        lxspan[i]=lxspan[i]+1;
    }
    lxspan[i]/=(HWid+1);
  }
  double* winf=NewWindow(wt, Wid, 0, &windp);
  double* wini=NewWindow(wtHann, Wid, NULL, NULL);
  for (int i=0; i<Wid; i++)
    if (winf[i]!=0) wini[i]=wini[i]/winf[i];
  AllocateFFTBuffer(Wid, ldata, w, x);
  double* tmpdata=new double[HWid];
  memset(tmpdata, 0, HWid*sizeof(double));

  for (int i=0; i<Fr; i++)
  {
    if (lxspan[i]==0)
    {
      memcpy(&dataout[i*HWid], tmpdata, sizeof(double)*HWid);
      memset(tmpdata, 0, sizeof(double)*HWid);
      continue;
    }
    if (lxspan[i]==1)
    {
      memcpy(ldata, &data[i*HWid], Wid*sizeof(double));
      if (i>0)
        for (int k=0; k<HWid; k++)
          ldata[k]=ldata[k]*winf[k]*wini[k];
      for (int k=HWid; k<Wid; k++)
        ldata[k]=ldata[k]*winf[k]*wini[k];

      memcpy(&dataout[i*HWid], tmpdata, HWid*sizeof(double));
      for (int k=0; k<HWid; k++)
        dataout[i*HWid+k]+=ldata[k];
      memcpy(tmpdata, &ldata[HWid], HWid*sizeof(double));
      continue;
    }
    memcpy(ldata, &data[i*HWid], Wid*sizeof(double));
    if (i>0)
      for (int k=0; k<HWid; k++)
        ldata[k]=ldata[k]*winf[k];
    for (int k=HWid; k<Wid; k++)
      ldata[k]=ldata[k]*winf[k];

    RFFTC(ldata, NULL, NULL, Order, w, x, 0);

    if (!lspan[i][0]) x[0].x=x[0].y=0;
    for (int j=1; j<=HWid; j++)
      if (!lspan[i][j]) x[j].x=x[Wid-j].x=x[j].y=x[Wid-j].y=0;

    CIFFTR(x, Order, w, ldata);

    if (i>0)
      for (int k=0; k<HWid; k++)
        ldata[k]=ldata[k]*wini[k];
    for (int k=HWid; k<Wid; k++)
      ldata[k]=ldata[k]*wini[k];

    memcpy(&dataout[i*HWid], tmpdata, HWid*sizeof(double));
    for (int k=0; k<HWid; k++)
      dataout[i*HWid+k]+=ldata[k];
    memcpy(tmpdata, &ldata[HWid], HWid*sizeof(double));
  }
  memcpy(&dataout[Fr*HWid], tmpdata, sizeof(double)*HWid);
  memset(&dataout[Fr*HWid+HWid], 0, sizeof(double)*(Count-Fr*HWid-HWid));

  FreeFFTBuffer(ldata);
  delete[] lspan[0];
  delete[] lspan;
  delete[] lxspan;
  delete[] tmpdata;
  delete[] winf;
  delete[] wini;
}//TFFilter
//version on integer data, where BytesPerSample specified the integer format.
void TFFilter(void* data, void* dataout, int BytesPerSample, int Count, int Wid, TTFSpans* Spans, bool Pass, WindowType wt, double windp, int Sps, int TOffst)
{
  int HWid=Wid/2;
  int Fr=Count/HWid-1;
  int Order=log2(Wid);

  int** lspan=new int*[Fr];
  double* lxspan=new double[Fr];

  lspan[0]=new int[Fr*Wid];
  for (int i=1; i<Fr; i++)
    lspan[i]=&lspan[0][i*Wid];

  //fill local filter span table
  if (Pass)
    memset(lspan[0], 0, sizeof(int)*Fr*Wid);
  else
    for (int i=0; i<Fr; i++)
      for (int j=0; j<Wid; j++)
        lspan[i][j]=1;

  for (int i=0; i<Spans->Count; i++)
  {
    int lx1, lx2, ly1, ly2;
    lx1=(Spans->Items[i].T1-TOffst)/HWid-1;
    lx2=(Spans->Items[i].T2-1-TOffst)/HWid;
    ly1=Spans->Items[i].F1*2/Sps*HWid+0.001;
    ly2=Spans->Items[i].F2*2/Sps*HWid+1;
    if (lx1<0) lx1=0;
    if (lx2>=Fr) lx2=Fr-1;
    if (ly1<0) ly1=0;
    if (ly1>HWid) ly1=HWid;
    if (Pass)
      for (int x=lx1; x<=lx2; x++)
        for (int y=ly1; y<=ly2; y++)
          lspan[x][y]=1;
    else
      for (int x=lx1; x<=lx2; x++)
        for (int y=ly1; y<=ly2; y++)
          lspan[x][y]=0;
  }
  for (int i=0; i<Fr; i++)
  {
    lxspan[i]=0;
    for (int j=0; j<=HWid; j++)
    {
      if (lspan[i][j])
        lxspan[i]=lxspan[i]+1;
    }
    lxspan[i]/=(HWid+1);
  }
  double* winf=NewWindow(wt, Wid, 0, &windp);
  double* wini=NewWindow(wtHann, Wid, NULL, NULL);
  for (int i=0; i<Wid; i++)
    if (winf[i]!=0) wini[i]=wini[i]/winf[i];
  AllocateFFTBuffer(Wid, ldata, w, x);
  double* tmpdata=new double[HWid];
  memset(tmpdata, 0, HWid*sizeof(double));

  for (int i=0; i<Fr; i++)
  {
    if (lxspan[i]==0)
    {
      DoubleToInt(&((char*)dataout)[i*HWid*BytesPerSample], BytesPerSample, tmpdata, HWid);
      memset(tmpdata, 0, sizeof(double)*HWid);
      continue;
    }
    if (lxspan[i]==1)
    {
      IntToDouble(ldata, &((char*)data)[i*HWid*BytesPerSample], BytesPerSample, Wid);
      if (i>0)
        for (int k=0; k<HWid; k++)
          ldata[k]=ldata[k]*winf[k]*wini[k];
      for (int k=HWid; k<Wid; k++)
        ldata[k]=ldata[k]*winf[k]*wini[k];

      for (int k=0; k<HWid; k++) tmpdata[k]+=ldata[k];
      DoubleToInt(&((char*)dataout)[i*HWid*BytesPerSample], BytesPerSample, tmpdata, HWid);
      memcpy(tmpdata, &ldata[HWid], HWid*sizeof(double));
      continue;
    }
    IntToDouble(ldata, &((char*)data)[i*HWid*BytesPerSample], BytesPerSample, Wid);
    if (i>0)
      for (int k=0; k<HWid; k++)
        ldata[k]=ldata[k]*winf[k];
    for (int k=HWid; k<Wid; k++)
      ldata[k]=ldata[k]*winf[k];

    RFFTC(ldata, NULL, NULL, Order, w, x, 0);

    if (!lspan[i][0]) x[0].x=x[0].y=0;
    for (int j=1; j<=HWid; j++)
      if (!lspan[i][j]) x[j].x=x[Wid-j].x=x[j].y=x[Wid-j].y=0;

    CIFFTR(x, Order, w, ldata);

    if (i>0)
      for (int k=0; k<HWid; k++)
        ldata[k]=ldata[k]*wini[k];
    for (int k=HWid; k<Wid; k++)
      ldata[k]=ldata[k]*wini[k];


    for (int k=0; k<HWid; k++)
      tmpdata[k]+=ldata[k];
    DoubleToInt(&((char*)dataout)[i*HWid*BytesPerSample], BytesPerSample, tmpdata, HWid);
    memcpy(tmpdata, &ldata[HWid], HWid*sizeof(double));
  }
  DoubleToInt(&((char*)dataout)[Fr*HWid*BytesPerSample], BytesPerSample, tmpdata, HWid);
  memset(&((char*)dataout)[(Fr*HWid+HWid)*BytesPerSample], 0, BytesPerSample*(Count-Fr*HWid-HWid));

  FreeFFTBuffer(ldata);

  delete[] lspan[0];
  delete[] lspan;
  delete[] lxspan;
  delete[] tmpdata;
  delete[] winf;
  delete[] wini;
}//TFFilter