#include "real.h"
#include "utils.h"
#include <math.h>
#include "sms.h"
#include "buffer.h"
#include "defs.h"
#include "limits.h"

using namespace std;
namespace _sbsms_ {

#define SMS_BUF_SIZE 4096

sms :: sms(int N, unsigned short M, unsigned short  M_MAX, int res, int latency, real p, real q, real pad, int Nlo, int channels, TrackAllocator *ta, PeakAllocator *pa) {
  this->N = N;
  this->M = M;
  Nover2 = N/2;
  this->p = 1.0/p;
  this->q = q;
  this->res = res;
  this->m = (real)M/(real)M_MAX;
  this->channels = channels;
  this->ta = ta;
  this->pa = pa;
  terminal = (M == M_MAX);

  datasize = sizeof(char) + 6*sizeof(unsigned short);
  pData = (char*)calloc(datasize,sizeof(char));

  // Phase lock channels
  bPhaseLock = 0;
  // Stitch bands together
  bStitch = 1;
  // Threshold (relative to maximum magnitude) for finding peaks
  peakThresh = .000005;
  // Threshold (in trackpoint units) for starting a track
  startThresh = 8.0*sqrt(pad/(real)N) * peakThresh;
  // Maximum figure of merit for continuing a track (see merit)
  maxMerit2 = square(.11);
  // Maximum differency in frequency for continuing a track
  maxDF2 = square(.08);
  // Coefficient for dB magnitude changes in figure of merit for continuing a track
  dMCoeff = 0.01;
  // Maximum increase in dB for continuing a track
  maxdBIncr = (12.0 - 6.0*m);
  // Maximum difference in dB for stitching two subband tracks together
  maxdBIncrStitch = (12.0 - 6.0*m);
  // Maximum figure of merit for matching tracks for phase locking
  maxMerit2PhaseLock = square(.01);
  // Maximum differene in frequency for matching tracks for phase locking
  maxDF2PhaseLock = square(.005);
  // Coefficient for dB magnitude changes in figure of merit for phase locking
  dMCoeffPhaseLock = 0.0005;
  // Maximum figure of merit for matching peaks for band stitching
  maxMerit2Match = square(.08);
  // Maximum difference in frequency for matching peaks for band stitching
  maxDF2Match = square(.04);
  // Coefficient for dB magnitude changes in figure of merit for band stitching
  dMCoeffMatch = 0.02;
    // Minimum number of trackpoints in a track for synthesis
  minNpts = 4;
  // Maximum width of a peak for determining spectral energy
  peakWidth = 2*((round2int(pad*(real)N/96.0)+1)/2);
  // Beginning and end of band
  if(terminal) kStart = 1; 
  else kStart = N/4-(int)(0.5*round2int((real)peakWidth*(real)N/(real)Nlo)+6);
  if(M==1) kEnd = N/2;
  else kEnd = N/2-peakWidth;
  // Maximum frequency for matching peaks for band stitching
  maxFMatch = (0.5*kEnd+8.0)*TWOPI/(real)N;
  // Minimum frequency for matching peaks for band stitching
  minFMatch = (2.0*kStart-16.0)*TWOPI/(real)N;
  // Minimum ratio of peak "top" to total peak energy
  minPeakTopRatio = 0.05;
  // Minimum ratio of low frequency resolution/high frequency resolution peaks for determining peaks
  // Ratio for favoring continuing a track in local band over stitching to another band
  localFavorRatio = 1.1;
  // Maximum ratio of (peak energy given to other peaks) to (peak energy)
  maxDecRatio = 1.5;

  // Normalization from spectrum energy to peak amplitude
  if(SBSMS_WINDOW == SBSMS_HANN)
    mNorm = 1.0/6.0 * pad * square(8.0 / (real)(N));
  else if(SBSMS_WINDOW == SBSMS_HAMMING)
    mNorm = 0.18141196440283 * pad * square(8.0 / (real)(N));
  else
    abort();

  // maximum magnitude (for determining peaks)
  magmax = 0;

  assigntime[0] = 0;
  assigntime[1] = 0;
  marktime[0] = 0;
  marktime[1] = 0;
  currtime = 0;
  synthtime = 0;

  trax = (list<track*>**)calloc(2,sizeof(list<track*>*));
  trax[0] = new list<track*>;
  trax[1] = new list<track*>;
  peak1 = (real*)calloc(2*N,sizeof(real));
  dec = (real*)calloc(N,sizeof(real));
  mag0[0] = (real*)calloc(N,sizeof(real));
  mag0[1] = (real*)calloc(N,sizeof(real));
  mag1[0] = (real*)calloc(N,sizeof(real));
  mag1[1] = (real*)calloc(N,sizeof(real));
  mag2[0] = (real*)calloc(N,sizeof(real));
  mag2[1] = (real*)calloc(N,sizeof(real));
  trackPointListBuffer[0] = new TrackPointListBuffer();
  trackPointListBuffer[1] = new TrackPointListBuffer();

  h2cum = 0;    
  sines2 = new SampleBuf(0);
  outMixer = sines2;
  
  x0[0] = grain :: create(N,1,SBSMS_WINDOW);
  x0[1] = grain :: create(N,1,SBSMS_WINDOW);

  x1[0] = grain :: create(N,1,SBSMS_WINDOW);
  x1[1] = grain :: create(N,1,SBSMS_WINDOW);

  x2[0] = grain :: create(N,1,SBSMS_WINDOW);
  x2[1] = grain :: create(N,1,SBSMS_WINDOW);

  bPeakSet = false;

#ifdef MULTITHREADED
  pthread_mutex_init(&dataMutex,NULL);
  pthread_mutex_init(&tplbMutex[0],NULL);
  pthread_mutex_init(&tplbMutex[1],NULL);
  pthread_mutex_init(&bufferMutex,NULL);
  pthread_mutex_init(&trackMutex[0],NULL);
  pthread_mutex_init(&trackMutex[1],NULL);
#endif
}

sms :: ~sms()
{
  reset();
  free(pData);
  free(mag0[0]);
  free(mag0[1]);
  free(mag1[0]);
  free(mag1[1]);
  free(mag2[0]);
  free(mag2[1]);
  free(dec);
  free(peak1);
  grain :: destroy(x0[0]);
  grain :: destroy(x0[1]);
  grain :: destroy(x1[0]);
  grain :: destroy(x1[1]);
  grain :: destroy(x2[0]);
  grain :: destroy(x2[1]);
  delete sines2;
  for(int c=0;c<channels;c++) {
    for(long k=trackPointListBuffer[c]->readPos; k<trackPointListBuffer[c]->writePos; k++) {
      tplist *tpl = trackPointListBuffer[c]->read(k);
      for(tplist::iterator tpi = tpl->begin();
	  tpi != tpl->end();
	  tpi++) {
	delete (*tpi);
      }
    }
  }
  delete trackPointListBuffer[0];
  delete trackPointListBuffer[1];
  delete trax[0];
  delete trax[1];
  free(trax);
}

void sms :: reset()
{
  assigntime[0] = 0;
  assigntime[1] = 0;
  marktime[0] = 0;
  marktime[1] = 0;
  currtime = 0;
  synthtime = 0;
  h2cum = 0;
  samplePos = 0;

  for(int c=0;c<channels;c++) {
    for(list<track*>::iterator tt=trax[c]->begin(); 
	tt != trax[c]->end(); 
	) {
      track *t = (*tt);      
      list<track*>::iterator eraseMe = tt;
      tt++;
      trax[c]->erase(eraseMe);
      ta->destroy(t);
    }
  }
  
  hSynth.clear();
  hAssign[0].clear();
  hAssign[1].clear();
  sines2->clear();
}


//1/65535
#define LOG_MIN (-11.09033963005365f)
//4
#define LOG_MAX 1.38629436111989f

unsigned short sms :: encodeRealLog(real x)
{
  x = log(x);
  if(x<LOG_MIN) return 0;
  else if(x>LOG_MAX) return USHRT_MAX;
  else return (unsigned short)round2int((real)USHRT_MAX*(x-LOG_MIN)/(LOG_MAX-LOG_MIN));
}

real sms :: decodeRealLog(unsigned short x)
{
  return exp(LOG_MIN + (LOG_MAX-LOG_MIN)*(real)x/(real)USHRT_MAX);
}

unsigned short sms :: encodeReal(real x, real min, real max)
{
  if(x<min) return 0;
  else if(x>max) return USHRT_MAX;
  else return (unsigned short)round2int((real)USHRT_MAX*(x-min)/(max-min));
}

real sms :: decodeReal(unsigned short x, real min, real max)
{
  return min + (max-min)*(real)x/(real)USHRT_MAX;
}

void sms :: readTrackPointsFromFile(FILE *fp)
{
  unsigned short h1;
  char *data = pData;
  fread(data,sizeof(unsigned short)+sizeof(char),1,fp);
  h1 = *((unsigned short*)data); data += sizeof(unsigned short);
#ifdef MULTITHREADED
  pthread_mutex_lock(&dataMutex);
#endif
  hSynth.write(h1);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&dataMutex);
#endif
  char ch = *((char*)data);
  channels = ch;
  for(int c=0;c<channels;c++) {
    unsigned short nTracks = 0;
    fread(&nTracks,sizeof(unsigned short),1,fp);
    for(int k=0;k<nTracks;k++) {
      unsigned short index;
      unsigned short precursorIndex;
      char flags;
      bool tailStart;
      bool tailEnd;
      bool bStart;
      bool bEnd;
      tpoint *tp = new tpoint();
      
      char *data = pData;
      fread(data,datasize,1,fp);
      flags = *((char*)data); data += sizeof(char);
      index = *((unsigned short*)data); data += sizeof(unsigned short);
      precursorIndex = *((unsigned short*)data); data += sizeof(unsigned short);
      unsigned short f;
      unsigned short y;
      unsigned short y0;
      unsigned short ph;
      f = *((unsigned short*)data); data += sizeof(unsigned short);
      y = *((unsigned short*)data); data += sizeof(unsigned short);
      y0 = *((unsigned short*)data); data += sizeof(unsigned short);
      ph = *((unsigned short*)data);
      tp->f = decodeReal(f,0.0f,PI);
      tp->y = decodeRealLog(y)*(real)M;
      tp->y0 = decodeRealLog(y0)*(real)M;
      tp->ph = decodeReal(ph,-PI,PI);
      
      bStart = (flags & 1)?true:false;
      tailStart = (flags & 2)?true:false;
      bEnd = (flags & 4)?true:false;
      tailEnd = (flags & 8)?true:false;
      tp->h = h1;
      tp->M = M;
      
      track *t = ta->getTrack(index);
      if(t && !bStart) {
	t->push_back_tpoint(tp);
	if(bEnd) {
	  t->end = assigntime[c];
	}
	if(tailEnd) {
	  t->tailEnd = true;
	}
      } else {
	track *t1;
	if(precursorIndex != USHRT_MAX) {
	  track *precursor = ta->getTrack(precursorIndex);
	  t1 = ta->create(precursor,this,res,index);
	  if(precursor) precursor->descendant = t1;
	} else {
	  t1 = ta->create(NULL,this,res,index);
	}
	if(tailStart)
	  t1->tailStart = true;
	t1->push_back_tpoint(tp);
	t1->m_p = tp->ph;
	t1->m_pDescendant = tp->ph;
	t1->currtime = assigntime[c];
	t1->start = assigntime[c];
	addTrack(c,t1);
      }
    }
    assigntime[c]++;
  }
}

void sms :: zeroPad(long n)
{
  sines2->grow(n);
  sines2->writePos += n;
}

int sms :: writeTrackPointsToFile(FILE *fp)
{
  int bytes = 0;
#ifdef MULTITHREADED
  pthread_mutex_lock(&dataMutex);
#endif
  unsigned short h1 = hSynth.read(hSynth.readPos);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&dataMutex);
#endif
  if(fp) {
    fwrite(&h1,sizeof(unsigned short),1,fp); bytes += sizeof(unsigned short);
    char c = channels;
    fwrite(&c,sizeof(char),1,fp); bytes += sizeof(char);
    for(int c=0;c<channels;c++) {
      unsigned short nTracks = 0;
      long start = ftell(fp);
      fwrite(&nTracks,sizeof(unsigned short),1,fp); bytes += sizeof(unsigned short);
#ifdef MULTITHREADED
      pthread_mutex_lock(&trackMutex[c]);
#endif
      for(list<track*>::iterator tt=trax[c]->begin(); 
	  tt != trax[c]->end(); 
	  ) {
	track *t = (*tt);      
	if(t->end < synthtime) {
	  if(!t->descendant||t->descendant->isDone()) {
	    list<track*>::iterator eraseMe = tt;
	    tt++;
	    trax[c]->erase(eraseMe);
	    ta->destroy(t);
	  } else {
	    tt++;
	  }
	} else if(synthtime >= t->start) {
	  track *t = (*tt);      
	  t->currtime = synthtime;
	  trackpoint *tp = t->getTrackPoint(synthtime);
	  if(tp) {
	    char flags = 0;
	    bool bStart;
	    bool bEnd;
	    bool tailStart;
	    bool tailEnd;
	    if(t->isStart(synthtime))
	      bStart = true;
	    else
	      bStart = false;
	    if(t->tailStart && t->isStart(synthtime))
	      tailStart = true;
	    else
	      tailStart = false;
	    if(t->isEnd(synthtime))
	      bEnd = true;
	    else
	      bEnd = false;
	    if(t->tailEnd && t->isEnd(synthtime))
	      tailEnd = true;
	    else
	      tailEnd = false;
	    
	    if(bStart)
	      flags += 1;
	    if(tailStart)
	      flags += 2;
	    if(bEnd)
	      flags += 4;
	    if(tailEnd)
	      flags += 8;
	    
	    fwrite(&flags,sizeof(char),1,fp); bytes += sizeof(char);
	    fwrite(&(t->index),sizeof(unsigned short),1,fp); bytes += sizeof(unsigned short);
	    if(t->precursor) {
	      fwrite(&(t->precursor->index),sizeof(unsigned short),1,fp); bytes += sizeof(unsigned short);
	    } else {
	      unsigned short none = USHRT_MAX;
	      fwrite(&none,sizeof(unsigned short),1,fp); bytes += sizeof(unsigned short);
	    }
	    unsigned short f = encodeReal(tp->f,0.0f,PI);
	    unsigned short y = encodeRealLog(tp->y/(real)M);
	    unsigned short y0 = encodeRealLog(tp->y0/(real)M);
	    unsigned short ph = encodeReal(tp->ph,-PI,PI);
	    fwrite(&f,sizeof(unsigned short),1,fp); bytes += sizeof(unsigned short);
	    fwrite(&y,sizeof(unsigned short),1,fp); bytes += sizeof(unsigned short);
	    fwrite(&y0,sizeof(unsigned short),1,fp); bytes += sizeof(unsigned short);
	    fwrite(&ph,sizeof(unsigned short),1,fp); bytes += sizeof(unsigned short);
	    nTracks++;
	  }
	  tt++;
	} else {
	  break;
	}
      }
#ifdef MULTITHREADED
      pthread_mutex_unlock(&trackMutex[c]);
#endif
      long end = ftell(fp);
      fseek(fp,start,SEEK_SET);
      fwrite(&nTracks,sizeof(unsigned short),1,fp);
      fseek(fp,end,SEEK_SET);    
    }
  }
#ifdef MULTITHREADED
  pthread_mutex_lock(&dataMutex);
#endif
  hSynth.advance(1);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&dataMutex);
#endif
  synthtime++;
  return bytes;
}

void sms :: setH(unsigned short h)
{
#ifdef MULTITHREADED
  pthread_mutex_lock(&dataMutex);
#endif
  hAssign[0].write(h);
  hAssign[1].write(h);
  hSynth.write(h);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&dataMutex);
#endif
}

long sms :: addTrackPoints(grain *g0, grain *g1, grain *g2)
{
  // set magnitude profile of signel constant frequency peak
  if(!bPeakSet) {
    for(int k=-Nover2;k<=Nover2;k++) {
      peak1[k+N] = norm2(g1->peak[(k+N)%N]);
    }
    bPeakSet = true;
  }

  setH(g1->h);
  // split channels
  c2evenodd(g0, x0[0], x0[1]);
  c2evenodd(g1, x1[0], x1[1]);
  c2evenodd(g2, x2[0], x2[1]);

  for(int c=0;c<channels;c++) {
    calcmags(mag0[c], x0[c],p);
    calcmags(mag1[c], x1[c],1);
    calcmags(mag2[c], x2[c],q);
    extractTrackPoints(x1[c],mag0[c],mag1[c],mag2[c],currtime,trackPointListBuffer[c],g1->h,c);
  }
  currtime++;
  return 1;
}

real sms :: merit(trackpoint *tp0, trackpoint *tp1, real m0, real m1, real dti2, real dMCoeff, real *df, real maxDF2)
{
  (*df) = m1*tp1->f - m0*tp0->f;
  real df2 = square(*df);
  if(df2 > maxDF2) return df2;
  real dM = dBApprox((m1*tp1->y)/(m0*tp0->y));
  return (df2+square(dMCoeff*dM));
}

bool sms :: nearestTrackPoint(tplist *tpl, trackpoint *tp0, real m0, real m1, real dti2, tplist::iterator *minpos, real *minMerit2, real maxMerit2, real maxDF2) 
{
  (*minMerit2) = 1e9;
  if(tpl == NULL) return false;
  bool bSet = false;

  for(tplist::iterator tpi=tpl->begin(); 
      tpi!=tpl->end();
      ++tpi) {

    trackpoint *tp1 = (*tpi);
    real df;
    real Merit2 = merit(tp0,tp1,m0,m1,dti2,dMCoeff,&df,maxDF2);
    if(m0!=m1) Merit2*=localFavorRatio;
    if(Merit2 < (*minMerit2) && Merit2 < maxMerit2 && square(df) < maxDF2) {
      (*minMerit2) = Merit2;
      (*minpos) = tpi;
      bSet = true;
    }
  }
  return bSet;
}

bool sms :: contTrack(track *t, trackpoint *tp, int c)
{
  bool allowCont = true;
  trackpoint *last = t->back();
  real dbIncr = dBApprox(tp->y/last->y);
#ifdef MULTITHREADED
  pthread_mutex_lock(&trackMutex[c]);
#endif
  if(dbIncr > maxdBIncr) {
    tp->y0 = last->y;
    t->push_back(tp);
  } else {
    t->push_back(tp);
  }
#ifdef MULTITHREADED
  pthread_mutex_unlock(&trackMutex[c]);
#endif
  return allowCont;
}      

bool sms :: adoptTrack(track *precursor, 
		       sms *lender,
		       trackpoint *tp,
		       real m,
		       real dt,
		       int c)
{
#ifdef MULTITHREADED
  pthread_mutex_lock(&lender->trackMutex[c]);
#endif
  trackpoint *last = precursor->back();
#ifdef MULTITHREADED
  pthread_mutex_unlock(&lender->trackMutex[c]);
#endif
  real dbIncr = dBApprox((m*tp->y)/last->y);

  bool bDiscont;
  if(dbIncr > maxdBIncrStitch) {
    bDiscont = true;
  } else {
    bDiscont = false;
  }
  if(tp->M>last->M) {
    if(dt==1.0) {
      if(lender->res==2) {
	trackpoint *tp0 = new trackpoint();
	tp0->h = tp->h*2/lender->res;
	tp0->y = 0.5*tp->y;
	if(bDiscont)
	  tp0->y0 = last->y;
	else 
	  tp0->y0 = tp0->y;
	tp0->f = 0.5*tp->f;
	tp0->ph = tp->ph;
	tp0->time = last->time+1;
	tp0->M = last->M;
#ifdef MULTITHREADED
	pthread_mutex_lock(&lender->trackMutex[c]);
#endif
	precursor->push_back(tp0);
#ifdef MULTITHREADED
	pthread_mutex_unlock(&lender->trackMutex[c]);
#endif
	if(!tp->owner) 
	  tp->bDelete = true;
      } else {
	trackpoint *tpend = new trackpoint();
	tpend->h = tp->h*2/lender->res;
	tpend->y = 0;
	tpend->y0 = 0;
	tpend->f = 0.5*tp->f;
	tpend->ph = tp->ph;
	tpend->time = last->time+1;
	tpend->M = last->M;
	
	trackpoint *tp0 = new trackpoint();
	tp0->h = last->h*lender->res/2;
	tp0->y = 0;
	tp0->y0 = 0;
	tp0->f = last->f*2.0;
	tp0->ph = last->ph;
	tp0->time = last->time/lender->res;
	tp0->M = M;
	track *t = ta->create(precursor,this,res);
#ifdef MULTITHREADED
	pthread_mutex_lock(&lender->trackMutex[c]);
#endif
	precursor->push_back(tpend);
	precursor->endTrack(false);
	precursor->descendant = t;
#ifdef MULTITHREADED
	pthread_mutex_unlock(&lender->trackMutex[c]);
#endif
	t->startTrack(tp0,false);
	if(bDiscont)
	  tp->y0 = 2.0*last->y;
	t->push_back(tp);
	addTrack(c,t);
      }
    } else if(dt==2.0) {
      track *t = ta->create(precursor,this,res);
      trackpoint *tpend0 = new trackpoint();
      trackpoint *tpend1 = new trackpoint();
      trackpoint *tp0 = new trackpoint();

      if(bDiscont)
	tp->y0 = 2.0*last->y;

      tp0->h = (last->h*lender->res)/2;
      tp0->y = 0;
      tp0->y0 = 0;
      tp0->f = last->f*2.0f;
      tp0->ph = last->ph;
      tp0->M = M;
      tp0->time = last->time/lender->res;
      
      tpend0->h = last->h;
      tpend0->y = 0.5f*last->y;
      tpend0->y0 = tpend0->y;
      tpend0->f = 0.5f*(last->f+0.5f*tp->f);
      tpend0->ph = canon(last->ph + 0.5f*(last->f+tpend0->f)*last->h);
      tpend0->time = last->time+1;
      tpend0->M = last->M;
      
      tpend1->h = (tp->h*2)/lender->res;
      tpend1->y = 0;
      tpend1->y0 = 0;
      tpend1->f = 0.5f*tp->f;
      tpend1->ph = tp->ph;
      tpend1->time = last->time+2;
      tpend1->M = last->M;
      
      t->startTrack(tp0,false);
      t->push_back(tp);
#ifdef MULTITHREADED
      pthread_mutex_lock(&lender->trackMutex[c]);
#endif
      precursor->push_back(tpend0);
      precursor->push_back(tpend1);
      precursor->endTrack(false);
      precursor->descendant = t;
#ifdef MULTITHREADED
      pthread_mutex_unlock(&lender->trackMutex[c]);
#endif
      addTrack(c,t);
    } else {
      abort();
    }
  } else {
    track *t = ta->create(precursor,this,res);

    if(bDiscont)
      tp->y0 = 0.5f*last->y;

    trackpoint *tp0 = new trackpoint();
    trackpoint *tpend = new trackpoint();
    
    tp0->h = (last->h*2)/res;
    tp0->y = 0;
    tp0->y0 = 0;
    tp0->f = 0.5f*last->f;
    tp0->ph = last->ph;
    tp0->time = last->time*res;
    tp0->M = M;
    
    tpend->h = (tp->h*res)/2;
    tpend->y = 0;
    tpend->y0 = 0;
    tpend->f = tp->f*2.0f;
    tpend->time = last->time+1;
    tpend->ph = tp->ph;
    tpend->M = last->M;
    
    t->startTrack(tp0,false);
    if(res==2) {
      trackpoint *tp1 = new trackpoint();
      tp1->h = (last->h*2)/res;
      tp1->y = 0.5f*tp->y0;
      tp1->y0 = tp1->y;
      tp1->f = 0.5f*(0.5f*last->f+tp->f);
      tp1->ph = canon(tp0->ph + 0.5f*(tp0->f + tp1->f)*last->h);
      tp1->time = last->time*res+1;
      tp1->M = M;
      t->push_back(tp1);
    }
    t->push_back(tp);
#ifdef MULTITHREADED
    pthread_mutex_lock(&lender->trackMutex[c]);
#endif
    precursor->push_back(tpend);
    precursor->endTrack(false);
    precursor->descendant = t;
#ifdef MULTITHREADED
    pthread_mutex_unlock(&lender->trackMutex[c]);
#endif
    addTrack(c,t);
  }
  return true;
}

void sms :: markDuplicatesLo(long offset, sms *lo, long offsetlo, int c)
{
  if(!lo) return;
#ifdef MULTITHREADED
  pthread_mutex_lock(&dataMutex);
#endif
  unsigned short ha = hAssign[c].read(hAssign[c].readPos);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&dataMutex);
#endif

#ifdef MULTITHREADED
  pthread_mutex_lock(&lo->tplbMutex[c]);
#endif
  tplist *trackPointsL1 = 
    lo->trackPointListBuffer[c]->read(lo->trackPointListBuffer[c]->readPos+offset/res+offsetlo);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&lo->tplbMutex[c]);
#endif
#ifdef MULTITHREADED
    pthread_mutex_lock(&tplbMutex[c]);
#endif
  tplist *trackPointsM1 = 
    trackPointListBuffer[c]->read(trackPointListBuffer[c]->readPos+offset);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&tplbMutex[c]);
#endif
  
  tplist trackPoints0;
  
  for(tplist::iterator tpi=trackPointsM1->begin(); 
      tpi!=trackPointsM1->end();
      tpi++) {
    
    if((*tpi)->f > maxFMatch) break;
    trackPoints0.push_back((*tpi));
  }
  
  bool bDone = false;
  bool bLastDitch = false;
  
  while(!bDone) {      
    int nToCont = 0;
    int nCont = 0;
    
    for(tplist::iterator tpi = trackPoints0.begin(); 
	tpi!=trackPoints0.end();
	tpi++) {
      real F;
      tplist::iterator minL0; bool minL0Set = nearestTrackPoint(trackPointsL1,*tpi,1.0,0.5,1.0,&minL0,&F,maxMerit2Match, maxDF2Match);
      if(minL0Set) {
	nToCont++;
	(*tpi)->cont = (*minL0);
      } else {
	(*tpi)->cont = NULL;
      }
    }
      
    if(trackPointsL1 && bStitch) {
      for(tplist::reverse_iterator tpi = trackPointsL1->rbegin(); 
	  tpi!=trackPointsL1->rend();
	  tpi++) {      
	real F;
	(*tpi)->cont = NULL;
	if((*tpi)->f < minFMatch) break;
	tplist::iterator minL1; bool minL1Set = nearestTrackPoint(&trackPoints0,*tpi,0.5,1.0,1.0,&minL1,&F,maxMerit2Match, maxDF2Match);
	if(minL1Set) (*tpi)->cont = *minL1;
      }
    }
    
    for(tplist::iterator tpi = trackPoints0.begin(); 
	tpi!=trackPoints0.end();
	) {
      trackpoint *tp0 = (*tpi);
      trackpoint *tp1 = tp0->cont;
      if(tp1 != NULL) {
	if(bLastDitch || tp1->cont == tp0) {
	  nCont++;
	  bool bAlreadyMarked = false;
	  if(offset%res == 0) {
	    if(tp1->dup[1] != NULL || tp0->dup[1] != NULL)
	      bAlreadyMarked = true;
	  } else {
	    if(tp1->dup[2-2*offsetlo] != NULL || tp0->dup[2*offsetlo] != NULL)
	      bAlreadyMarked = true;
	  }
	  if(!bAlreadyMarked) {
	    if(offset%res == 0) {
	      tp1->dup[1] = tp0;
	      tp0->dup[1] = tp1;
	    } else {
	      tp1->dup[2-2*offsetlo] = tp0;
	      tp0->dup[2*offsetlo] = tp1;
	    }
	  }
	  tplist::iterator eraseMe = tpi;
	  tpi++;
	  trackPoints0.erase(eraseMe);	    
	  continue;
	}
      }
      tpi++;
    }
    
    bDone = (nToCont-nCont == 0);
    bLastDitch = (!bDone && nCont==0);
  }
  marktime[c]++;
}

void sms :: markDuplicates(long offset, sms *hi, sms *lo, int c)
{
  if(offset%res==0) {
    markDuplicatesLo(offset,lo,offset%res,c);
  } else {
    markDuplicatesLo(offset,lo,0,c);
    markDuplicatesLo(offset,lo,1,c);
  }
}

void sms :: addTrack(int c, track *t)
{
#ifdef MULTITHREADED
  pthread_mutex_lock(&trackMutex[c]);
#endif
  list<track*>::reverse_iterator i=trax[c]->rbegin();
  while(i!=trax[c]->rend()) {
    track *t0 = *i;
    if(t->start >= t0->start) {
      break;
    }
    i++;
  }
  trax[c]->insert(i.base(),t);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&trackMutex[c]);
#endif
}

void sms :: deleteTrackPoint(trackpoint *tp)
{
  delete tp;
}

bool sms :: connectTrackPoints(trackpoint *tp0, trackpoint *tp1, sms *hi, sms *lo, real dtlo, int c)
{
  bool isConn;
  bool allowCont;
  if(tp0->M == tp1->M) {
    allowCont = contTrack(tp0->owner, tp1, c);
    isConn = allowCont;
  } else if(tp0->M < tp1->M) {
    isConn = true;
    allowCont = lo->adoptTrack(tp0->owner, this, tp1, 0.5, dtlo, c);
  } else {
    isConn = true;
    allowCont = hi->adoptTrack(tp0->owner, this, tp1, 2.0, 1.0, c);
  }
  tp0->bConnected = true;
  if(isConn) {
    tp1->bConnected = true;
    if(tp0->dupcont != NULL) {
      trackpoint *dup = tp0->dupcont;
      dup->bConnected = true;
      if(!dup->owner)
	dup->bDelete = true;
    }
    
    trackpoint *dup2 = tp0->dup[2];
    if(dup2 && dup2 != tp1 && !dup2->owner) {
      dup2->bDelete = true;
    }

    for(int d=0;d<3;d++) {
      if(tp1->dup[d] && !tp1->dup[d]->owner && (d<2 || tp1->dup[d]->M < tp1->M)) {
	tp1->dup[d]->bDelete = true;
      }
    }
  }
  return allowCont;
}

void sms :: advanceTrackPoints(int c)
{
#ifdef MULTITHREADED
    pthread_mutex_lock(&tplbMutex[c]);
#endif
    trackPointListBuffer[c]->advance(1);
#ifdef MULTITHREADED
    pthread_mutex_unlock(&tplbMutex[c]);
#endif
}

long sms :: assignTrackPoints(long offset, sms *hi, sms *lo, int c)
{  
  if(offset%res == 0)
    return assignTrackPoints_(offset,hi,lo,1.0,0,c);
  else
    return assignTrackPoints_(offset,hi,lo,2.0,1,c);
}

long sms :: assignTrackPoints_(long offset, sms *hi, sms *lo, real dtlo, long offsetlo, int c)
{
#ifdef MULTITHREADED
  pthread_mutex_lock(&dataMutex);
#endif
  unsigned short ha = hAssign[c].read(hAssign[c].readPos);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&dataMutex);
#endif

#ifdef MULTITHREADED
  if(hi) pthread_mutex_lock(&hi->tplbMutex[c]);
#endif
  tplist *trackPointsH1 = 
    hi?hi->trackPointListBuffer[c]->read(hi->trackPointListBuffer[c]->readPos+hi->res*offset):NULL;  
#ifdef MULTITHREADED 
  if(hi) pthread_mutex_unlock(&hi->tplbMutex[c]);
#endif

#ifdef MULTITHREADED
  if(lo) pthread_mutex_lock(&lo->tplbMutex[c]);
#endif
    tplist *trackPointsL1 = 
    lo?lo->trackPointListBuffer[c]->read(lo->trackPointListBuffer[c]->readPos+offset/res+offsetlo):NULL;
#ifdef MULTITHREADED
	if(lo) pthread_mutex_unlock(&lo->tplbMutex[c]);
#endif

#ifdef MULTITHREADED
  pthread_mutex_lock(&tplbMutex[c]);
#endif
  tplist *trackPointsM1 = 
    trackPointListBuffer[c]->read(trackPointListBuffer[c]->readPos+offset);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&tplbMutex[c]);	
#endif
  
  tplist trackPoints0;
  
#ifdef MULTITHREADED
  pthread_mutex_lock(&trackMutex[c]);
#endif
  for(list<track*>::iterator tt=trax[c]->begin(); 
      tt != trax[c]->end(); ) {
    track *t = (*tt);
    t->bEnd = true;
    if(t->isEnded() || t->back()->time+1 != assigntime[c]) { t->bEnd = false; tt++; continue; }
    trackPoints0.push_back(t->back());
    tt++;
  }
#ifdef MULTITHREADED
  pthread_mutex_unlock(&trackMutex[c]);
#endif

  for(tplist::iterator tpi=trackPointsM1->begin(); 
      tpi!=trackPointsM1->end();
      ) {
    trackpoint *tp = (*tpi);
    if(tp->bDelete) {
      tplist::iterator eraseMe = tpi;
      tpi++;
      trackPointsM1->erase(eraseMe);
      deleteTrackPoint(tp);
    } else {
      tpi++;
    }
  }
  
  if(trackPointsL1) {
    for(tplist::iterator tpi=trackPointsL1->begin(); 
	tpi!=trackPointsL1->end();
	) {
      trackpoint *tp = (*tpi);
      if(tp->bDelete) {
	tplist::iterator eraseMe = tpi;
	tpi++;
	trackPointsL1->erase(eraseMe);
	deleteTrackPoint(tp);
      } else {
	tpi++;
      }
    }
  }

  if(trackPointsH1) {
    for(tplist::iterator tpi=trackPointsH1->begin(); 
	tpi!=trackPointsH1->end();
	) {
      trackpoint *tp = (*tpi);
      if(tp->bDelete) {
	tplist::iterator eraseMe = tpi;
	tpi++;
	trackPointsH1->erase(eraseMe);
	deleteTrackPoint(tp);
      } else {
	tpi++;
	}
    }
  }

  bool bDone = false;
  bool bLastDitch = false;
  
  while(!bDone) {
    int nToCont = 0;
    int nCont = 0;
    
    for(tplist::iterator tpi=trackPointsM1->begin(); 
	tpi!=trackPointsM1->end();
	tpi++) {
      real F;	
      (*tpi)->bConnect = false;
      (*tpi)->bConnected = false;
      tplist::iterator minM1; bool minM1Set = nearestTrackPoint(&trackPoints0,*tpi,1.0,1.0,1.0,&minM1,&F,maxMerit2,maxDF2);
      if(minM1Set) (*tpi)->cont = *minM1;
      else (*tpi)->cont = NULL;
    }
    
    if(trackPointsL1 && bStitch) {
      for(tplist::reverse_iterator tpi = trackPointsL1->rbegin(); 
	  tpi!=trackPointsL1->rend();
	  tpi++) {      
	(*tpi)->cont = NULL;
	(*tpi)->bConnect = false;
	(*tpi)->bConnected = false;
	if((*tpi)->f < minFMatch) break;
	real F;
	tplist::iterator minL1; bool minL1Set = nearestTrackPoint(&trackPoints0,*tpi,0.5,1.0,square(1.0/dtlo),&minL1,&F,maxMerit2,maxDF2);
	if(minL1Set) (*tpi)->cont = *minL1;
      }
    }
    
    if(trackPointsH1 && bStitch) {
      for(tplist::iterator tpi = trackPointsH1->begin(); 
	  tpi!=trackPointsH1->end();
	  tpi++) {      
	(*tpi)->cont = NULL;
	(*tpi)->bConnect = false;
	(*tpi)->bConnected = false;
	real F;
	if((*tpi)->f < maxFMatch) {
	  tplist::iterator minH1; bool minH1Set = nearestTrackPoint(&trackPoints0,*tpi,2.0,1.0,1.0,&minH1,&F,maxMerit2,maxDF2);
	  if(minH1Set) (*tpi)->cont = *minH1;
	}
      }
    }
    
    for(tplist::iterator tpi = trackPoints0.begin(); 
	tpi!=trackPoints0.end();
	tpi++) {
      
      (*tpi)->dupcont = NULL;
      (*tpi)->bConnected = false;
      real FM0, FL0, FH0;
      tplist::iterator minM0; bool minM0Set = nearestTrackPoint(trackPointsM1,*tpi,1.0,1.0,1.0,&minM0,&FM0,maxMerit2,maxDF2);
      tplist::iterator minL0; bool minL0Set = nearestTrackPoint(trackPointsL1,*tpi,1.0,0.5,square(1.0/dtlo),&minL0,&FL0,maxMerit2,maxDF2);
      tplist::iterator minH0; bool minH0Set = nearestTrackPoint(trackPointsH1,*tpi,1.0,2.0,1.0,&minH0,&FH0,maxMerit2,maxDF2);
      
      if(minM0Set &&
	 (!bStitch
	  ||(FM0<=FH0 && FM0<=FL0)
	  ||(minL0Set && FL0<=FH0 && FL0<=FM0 && (*minL0)->dup[1-offsetlo] == (*minM0))
	  ||(minH0Set && FH0<=FL0 && FH0<=FM0 && (*minH0)->dup[1] == (*minM0)))
	 ) {
	if(offsetlo == 0 && minL0Set && (*minL0)->dup[1] == (*minM0)) {	    
	  (*tpi)->dupcont = *minL0;
	} else if(minH0Set && (*minH0)->dup[1] == (*minM0)) {
	  (*tpi)->dupcont = *minH0;
	}
	(*tpi)->contF = FM0;
	(*tpi)->cont = *minM0;
	nToCont++;
      } else if(bStitch && minL0Set && FL0<=FM0 && FL0<=FH0) {
	if(minM0Set && (*minL0)->dup[1-offsetlo] == (*minM0)) {
	  (*tpi)->dupcont = *minM0;
	}
	(*tpi)->contF = FL0;
	(*tpi)->cont = *minL0;
	nToCont++;
      } else if(bStitch && minH0Set && FH0<=FM0 && FH0<=FL0) {
	if(minM0Set && (*minH0)->dup[1] == (*minM0)) {
	  (*tpi)->dupcont = *minM0;
	}
	(*tpi)->contF = FH0;
	(*tpi)->cont = *minH0;
	nToCont++;
      } else {
	(*tpi)->cont = NULL;
      }
    }

    // Nominal connections - may be conflicts to be resolved
    for(tplist::iterator tpi = trackPoints0.begin(); 
	tpi!=trackPoints0.end();
	tpi++
	) {
      trackpoint *tp0 = (*tpi);
      trackpoint *tp1 = tp0->cont;
      if(tp1 != NULL) {
	if(bLastDitch || (tp1->cont == tp0)) {
	  tp1->bConnect = true;
	}
      }
    }
    
    // Make connections and resolve conflicts between duplicates and between last ditch connections
    for(tplist::iterator tpi = trackPoints0.begin(); 
	tpi!=trackPoints0.end();
	) {
      
      trackpoint *tp0 = (*tpi);
      trackpoint *tp1 = tp0->cont;
      
      if(tp0->bDelete) { 
	tplist::iterator eraseMe = tpi;
	tpi++;
	trackPoints0.erase(eraseMe);
	continue;
      }
      if(tp1 != NULL && tp1->bConnect) {
	if(tp0->dupcont != NULL && tp0->dupcont->bConnect) {
	  if(!tp1->bConnected && !tp0->dupcont->bConnected) {
	    if(!tp0->bConnected && !tp1->bDelete && (tp0->dupcont->cont == NULL || tp0->contF <= tp0->dupcont->cont->contF)) {
	      nCont++;
	      if(connectTrackPoints(tp0,tp1,hi,lo,dtlo,c))
		tp0->owner->bEnd = false;
	      tp0->dupcont->bConnect = false;
	    } else if(tp0->dupcont->cont != NULL && !tp0->dupcont->cont->bConnected && !tp0->dupcont->bDelete && !tp0->dupcont->cont->bDelete) {
	      nCont++;
	      if(connectTrackPoints(tp0->dupcont->cont,tp0->dupcont,hi,lo,dtlo,c))
		tp0->dupcont->cont->owner->bEnd = false;
	      tp1->bConnect = false;
	    }
	  }
	} else if(!tp0->bConnected && !tp1->bConnected && !tp1->bDelete) {
	  nCont++;
	  if(connectTrackPoints(tp0,tp1,hi,lo,dtlo,c)) {
	    tp0->owner->bEnd = false;
	  }
	}
      }
      if(tp0->bConnected || tp0->bDelete) {
	tplist::iterator eraseMe = tpi;
	tpi++;
	trackPoints0.erase(eraseMe);
      } else {
	tpi++;
      }
    }
    
    for(tplist::iterator tpi=trackPointsM1->begin(); 
	tpi!=trackPointsM1->end();
	) {
      trackpoint *tp = (*tpi);
      if(tp->bConnected ||tp->bDelete) {
	tplist::iterator eraseMe = tpi;
	tpi++;
	trackPointsM1->erase(eraseMe);
      } else {
	tpi++;
      }
      if(tp->bDelete) {
	deleteTrackPoint(tp);
      }
    }
    
    if(trackPointsL1) {
      for(tplist::iterator tpi=trackPointsL1->begin(); 
	  tpi!=trackPointsL1->end();
	  ) {      
	trackpoint *tp = (*tpi);
	if(tp->bConnected||tp->bDelete) {
	  tplist::iterator eraseMe = tpi;
	  tpi++;
	  trackPointsL1->erase(eraseMe);
	} else {
	  tpi++;
	}
	if(tp->bDelete) {
	  deleteTrackPoint(tp);
	}
      }
    }
    
    if(trackPointsH1) {
      for(tplist::iterator tpi=trackPointsH1->begin(); 
	  tpi!=trackPointsH1->end();
	  ) {    
	trackpoint *tp = (*tpi);
	if(tp->bConnected || tp->bDelete) {
	  tplist::iterator eraseMe = tpi;
	  tpi++;
	  trackPointsH1->erase(eraseMe);
	} else {
	  tpi++;
	}
	if(tp->bDelete) {
	  deleteTrackPoint(tp);
	}
      }
    }
    
    bDone = (nToCont-nCont == 0);
    bLastDitch = (!bDone && nCont==0);
  }
  
#ifdef MULTITHREADED
  pthread_mutex_lock(&trackMutex[c]);
#endif
  for(list<track*>::iterator tt=trax[c]->begin(); 
      tt != trax[c]->end(); ) {
    track *t = (*tt);      
    if(t->bEnd) {
      t->endTrack(true);
    }
    if(t->size() < minNpts && !t->descendant) {
      list<track*>::iterator eraseMe = tt;
      tt++;
      trax[c]->erase(eraseMe);
      ta->destroy(t);  
    } else {
      tt++;
    }
  }

#ifdef MULTITHREADED
  pthread_mutex_unlock(&trackMutex[c]);
#endif
  
#ifdef MULTITHREADED
  pthread_mutex_lock(&dataMutex);
#endif
  hAssign[c].advance(1);
  assigntime[c]++;
#ifdef MULTITHREADED
  pthread_mutex_unlock(&dataMutex);
#endif

  return 1;
}

void sms :: stereoPhaseLock()
{
  if(bPhaseLock && channels==2) {
    // Stereo phase locking
    tplist trackPoints0;
    tplist trackPoints1;
#ifdef MULTITHREADED
    pthread_mutex_lock(&trackMutex[0]);
    pthread_mutex_lock(&trackMutex[1]);
#endif
    for(list<track*>::iterator tt=trax[0]->begin(); 
	tt != trax[0]->end(); 
	tt++) {
      track *t = (*tt);
      trackpoint *tp = t->getTrackPoint(assigntime[0]);
      if(tp)
	trackPoints0.push_back(tp);
    }
    for(list<track*>::iterator tt=trax[1]->begin(); 
	tt != trax[1]->end(); 
	tt++) {
      track *t = (*tt);
      trackpoint *tp = t->getTrackPoint(assigntime[1]);
      if(tp)
	trackPoints1.push_back(tp);
    }
#ifdef MULTITHREADED
    pthread_mutex_unlock(&trackMutex[0]);
    pthread_mutex_unlock(&trackMutex[1]);
#endif
    
    bool bDone = false;
    bool bLastDitch = false;
    while(!bDone) {
      int nToCont = 0;
      int nCont = 0;
      
      for(tplist::iterator tpi = trackPoints0.begin(); 
	  tpi!=trackPoints0.end();
	  tpi++) {
	real F;
	tplist::iterator min1; bool min1Set = nearestTrackPoint(&trackPoints1,*tpi,1.0,1.0,1.0,&min1,&F,maxMerit2PhaseLock,maxDF2PhaseLock);
	(*tpi)->bConnected = false;
	if(min1Set) {
	  nToCont++;
	  (*tpi)->cont = (*min1);
	} else {
	  (*tpi)->cont = NULL;
	}
      }
      
      for(tplist::iterator tpi = trackPoints1.begin(); 
	  tpi!=trackPoints1.end();
	  tpi++) {
	real F;
	tplist::iterator min0; bool min0Set = nearestTrackPoint(&trackPoints0,*tpi,1.0,1.0,1.0,&min0,&F,maxMerit2PhaseLock,maxDF2PhaseLock);
	(*tpi)->bConnected = false;
	if(min0Set) {
	  (*tpi)->cont = (*min0);
	} else {
	  (*tpi)->cont = NULL;
	}
      }
      
      for(tplist::iterator tpi = trackPoints0.begin(); 
	  tpi!=trackPoints0.end();
	  ) {
	
	trackpoint *tp0 = (*tpi);
	trackpoint *tp1 = tp0->cont;
	
	if(tp1 != NULL && (bLastDitch || tp1->cont == tp0)) {
	  nCont++;
	  phaseLock(tp0,tp1,assigntime[0]);
	  tp0->bConnected = true;
	  tp1->bConnected = true;
	}
	if(tp0->bConnected) {
	  tplist::iterator eraseMe = tpi;
	  tpi++;
	  trackPoints0.erase(eraseMe);
	} else {
	  tpi++;
	}
      }
      
      for(tplist::iterator tpi=trackPoints1.begin(); 
	  tpi!=trackPoints1.end();
	  ) {
	trackpoint *tp = (*tpi);
	if(tp->bConnected) {
	  tplist::iterator eraseMe = tpi;
	  tpi++;
	  trackPoints1.erase(eraseMe);
	} else {
	  tpi++;
	}
      }

      bDone = (nToCont == nCont);
      bLastDitch = (!bDone && nCont==0);
    }
  }
}

long sms :: startNewTracks(long offset, int c)
{
#ifdef MULTITHREADED
  pthread_mutex_lock(&tplbMutex[c]);
#endif
  tplist *trackPoints = trackPointListBuffer[c]->read(trackPointListBuffer[c]->readPos+offset);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&tplbMutex[c]);
#endif

  for(tplist::iterator tpi=trackPoints->begin(); 
      tpi!=trackPoints->end();
      ) {
    trackpoint *tp = (*tpi);
    
    if(!tp->bDelete
       &&
       tp->y > startThresh*sqrtmagmax) {
      track *t = ta->create(NULL,this,res);
      t->startTrack(tp,true);
      addTrack(c,t);	
      for(int d=0;d<2;d++) {
	if(tp->dup[d])
	  if(!tp->dup[d]->owner)
	    tp->dup[d]->bDelete = true;
      }
    } else {
      deleteTrackPoint(tp);
    }                 
    tplist::iterator eraseMe = tpi;
    tpi++;
    trackPoints->erase(eraseMe);
  }
  return 1;
}

void sms :: phaseLock(trackpoint *tp0, trackpoint *tp1, long time)
{
  track *t0 = tp0->owner;
  track *t1 = tp1->owner;
    if(t0->isStart(time)) {
    t0->m_p = canon(t1->m_p + tp1->ph - tp0->ph);
  } else if(t1->isStart(time)) {
    t1->m_p = canon(t0->m_p + tp0->ph - tp1->ph);
  } 
  real f = 0.5*(tp1->f + tp0->f);
  tp0->f = f;
  tp1->f = f;
}

void sms :: synthTracks(real a)
{
#ifdef MULTITHREADED
  pthread_mutex_lock(&dataMutex);
#endif
  unsigned short h1 = hSynth.read(hSynth.readPos);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&dataMutex);
#endif
  real h2 = (a*(real)h1);			  
  h2cum += h2;
  int h2i = round2int(h2cum);
  h2cum -= h2i;
#ifdef MULTITHREADED
  pthread_mutex_lock(&bufferMutex);
#endif
  // extra slack for long fall times
  int grow = h2i<<1;
  sines2->N = grow;
  sines2->grow(grow);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&bufferMutex);
#endif

  for(int c=0;c<channels;c++) {
    list<track*> tracks;
#ifdef MULTITHREADED
    pthread_mutex_lock(&trackMutex[c]);
#endif
    for(list<track*>::iterator tt=trax[c]->begin();
	tt!=trax[c]->end(); ) {  
      track *t = (*tt);
      if(t->end <= synthtime) {
	if(!t->descendant||t->descendant->isDone()) {
	  list<track*>::iterator eraseMe = tt;
	  tt++;
	  trax[c]->erase(eraseMe);
	  ta->destroy(t);
	} else {
	  tt++;
	}
      } else if(synthtime >= t->start) {
	trackpoint *tp = t->getTrackPoint(synthtime);
	t->synth(sines2,sines2->writePos,c,synthtime,h2i);
	tt++;
      } else {
	break;
      }
    }
#ifdef MULTITHREADED
    pthread_mutex_unlock(&trackMutex[c]);
#endif   
  }

#ifdef MULTITHREADED
  pthread_mutex_lock(&bufferMutex);
#endif
  sines2->writePos += h2i;
#ifdef MULTITHREADED
  pthread_mutex_unlock(&bufferMutex);
#endif
  samplePos += h2i;
#ifdef MULTITHREADED
  pthread_mutex_lock(&dataMutex);
#endif
  hSynth.advance(1);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&dataMutex);
#endif
  synthtime++;
}

long sms :: n_readable()
{
#ifdef MULTITHREADED
  pthread_mutex_lock(&bufferMutex);
#endif
  long n = outMixer->n_readable();
#ifdef MULTITHREADED
  pthread_mutex_unlock(&bufferMutex);
#endif
  return n;
}

long sms :: read(audio *out, long n)
{
  long n_read;
#ifdef MULTITHREADED
  pthread_mutex_lock(&bufferMutex);
#endif
  n_read = outMixer->read(out, n); 
#ifdef MULTITHREADED
  pthread_mutex_unlock(&bufferMutex);
#endif
  return n_read;
}

void sms :: advance(long n) 
{
#ifdef MULTITHREADED
  pthread_mutex_lock(&bufferMutex);
#endif
  outMixer->advance(n);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&bufferMutex);
#endif
}

void sms :: c2evenodd(grain *eo, grain *even, grain *odd) {
  _c2evenodd(eo->freq, even->freq, odd->freq, eo->N);
}

void sms :: calcmags(real *mag, grain *g, real q) {
  real q2 = 1.0/q;
  for(int k=0;k<=Nover2;k++) {
    real m = norm2(g->freq[k])*q2;
    mag[k] = m;
    if(magmax < m) magmax = m;
  }
  sqrtmagmax = sqrt(magmax);
}

peak *sms :: makePeak(real *mag, real *mag2, int k)
{
  real v0,v1,v2;
  v0 = mag2[k-1];
  v1 = mag2[k];
  v2 = mag2[k+1];
  real y1 = v1-v0;
  real y2 = v2-v0;
  real a = 0.5*(y2 - 2*y1);
  real b = a==0?2.0:1-y1/a;
      
  peak *p = pa->create();
  p->x = k-1+0.5*b;
  p->k = k;
  p->y2 = v0-0.25*a*b*b;
  int k0 = round2int(p->x);
  real kf = k0<p->x?p->x-k0:k0-p->x;
  int k1 = k0<p->x?k0+1:k0-1;
  p->y = (1.0-kf)*mag[k0] + kf*mag[k1];
  p->tp = NULL;
  p->tp2 = NULL;
  p->tn = NULL;
  p->tn2 = NULL;
  return p;
}

void sms :: adjustPeaks(list<peak*> &peaks,
			real *mag0,
			real *mag1,
			real *mag2,
			real *dec,
			int peakWidth
			) {

  for(int k=0;k<N;k++) {
    dec[k] = 0.0;
  }

  // determine troughs and remove peaks based on minPeakTopRatio
  // don't determine maginitude yet
  for(list<peak *>::iterator tpi = peaks.begin();
      tpi != peaks.end();
      ) {
    list<peak*>::iterator eraseMe = tpi;
    peak *p = (*tpi);

    peak *pp = NULL;
    if(tpi!=peaks.begin()) {
      pp = *(--tpi);
      tpi++;
    }

    peak *pn = NULL;
    tpi++;
    if(tpi != peaks.end()) {
      pn = (*tpi);
    }

    if(p->tp && pp && pp->tn
       &&
       p->x - p->tp->x > p->x - p->tp2->x
       &&
       pp->tn->x - pp->x > pp->tn2->x - pp->x) {
    } else {
      p->tp = p->tp2;
    }
    
    if(p->tn && pn && pn->tp
       &&
       p->tn->x - p->x > p->tn2->x - p->x
       &&
       pn->x - pn->tp->x > pn->x - pn->tp2->x) {
    } else {
      p->tn = p->tn2;
    }

    peak *tp = p->tp;
    peak *tn = p->tn;

    int k1 = p->k;
    int k0 = tp->k;
    real kf0;
    if(k0 < k1-peakWidth) {
      k0 = k1-peakWidth;
      kf0 = 0;
    } else {
      kf0 = k0 > tp->x ? k0 - tp->x : tp->x - k0;
    }
    int k2 = tn->k;
    real kf2;
    if(k2 > k1+peakWidth) {
      k2 = k1+peakWidth;
      kf2 = 0;
    } else {
      kf2 = k2 > tn->x ? k2 - tn->x : tn->x - k2;
    }

    real m = 0.0f;
    real mtop = 0.0f;

    real mbase = mag2[k0] + mag2[k2];
    if(k0 < tp->x) {
      real m0 = ((mag2[k0])+(mag2[k0+1]))*(1.0-kf0);
      m += m0;
      mtop += max(0.0f,(m0 - mbase)*(1.0-kf0));
    } else {
      real m0 = ((mag2[k0])+(mag2[k0+1]));
      m += m0;
      mtop += max(0.0f,m0 - mbase);
      m0 = ((mag2[k0-1])+(mag2[k0]))*kf0;
      m += m0;
      mtop += max(0.0f,(m0 - mbase)*kf0);
    }
    
    if(k2 < tn->x) {
      real m0 = ((mag2[k2-1])+(mag2[k2]));
      m += m0;
      mtop += max(0.0,m0 - mbase);
      m0 = ((mag2[k2])+(mag2[k2+1]))*kf2;
      m += m0;
      mtop += max(0.0f,(m0 - mbase)*kf2);
    } else {
      real m0 = ((mag2[k2-1])+(mag2[k2]))*(1.0-kf2);
      m += m0;
      mtop += max(0.0f,(m0 - mbase)*(1.0-kf2));
    }
    
    for(int k=k0+1;k<k2-1;k++) {
      real m0 = ((mag2[k])+(mag2[k+1]));
      m += m0;
      mtop += max(0.0f,m0 - mbase);
    }

    if(mtop < minPeakTopRatio*m) {
      peaks.erase(eraseMe);
      if(pp && (!pn || pp->y > pn->y)) {
	pp->tn = p->tn;
	pp->tn2 = p->tn2;
      } else if(pn && (!pp || pn->y > pp->y)) {
	pn->tp = p->tp;
	pn->tp2 = p->tp2;
      }
    }
  }

  // determine magnitude
  for(list<peak *>::iterator tpi = peaks.begin();
      tpi != peaks.end();
      tpi++
      ) {
    peak *p = (*tpi);
    
    peak *tp = p->tp;
    peak *tn = p->tn;
       
    int k1 = p->k;
    int ko1 = k1 > p->x ? -1 : 1;
    real kf1 = k1 > p->x ? k1 - p->x : p->x - k1; 
    int k0 = tp->k;
    real kf0;
    if(k0 < k1-peakWidth) {
      k0 = k1-peakWidth;
      kf0 = 0;
    } else {
      kf0 = k0 > tp->x ? k0 - tp->x : tp->x - k0;
    }
    int k2 = tn->k;
    real kf2;
    if(k2 > k1+peakWidth) {
      k2 = k1+peakWidth;
      kf2 = 0;
    } else {
      kf2 = k2 > tn->x ? k2 - tn->x : tn->x - k2;
    }

    real m1 = 0;

    if(k0 < tp->x) {
      m1 += ((mag1[k0])+(mag1[k0+1]))*(1.0-kf0);
    } else {
      m1 += ((mag1[k0])+(mag1[k0+1]));
      m1 += ((mag1[k0-1])+(mag1[k0]))*kf0;
    }
    
    if(k2 < tn->x) {
      m1 += ((mag1[k2-1])+(mag1[k2]));
      m1 += ((mag1[k2])+(mag1[k2+1]))*kf2;
    } else {
      m1 += ((mag1[k2-1])+(mag1[k2]))*(1.0-kf2);
    }
    
    for(int k=k0+1;k<k2-1;k++) {
      m1 += ((mag1[k])+(mag1[k+1]));
    }
    
    m1 *= 0.5;

    for(int k=max(1,k1-peakWidth+1);k<=k0;k++) {
      real d = (1.0-kf1)*peak1[k-k1+N] + kf1*peak1[k-k1+N+ko1];
      real m = d*(p->y);
      m1 += m;
      dec[k] += m;
    }
    for(int k=k2;k<min(k1+peakWidth,Nover2-1);k++) {
      real d = (1.0-kf1)*peak1[k-k1+N] + kf1*peak1[k-k1+N+ko1];
      real m = d*(p->y);
      m1 += m;
      dec[k] += m;
    }
    p->m = m1;
  }

  for(list<peak *>::iterator tpi = peaks.begin();
      tpi != peaks.end();
      ) {
    peak *p = (*tpi);

    list<peak*>::iterator eraseMe = tpi;
    peak *pp = NULL;
    if(tpi!=peaks.begin()) {
      pp = *(--tpi);
      tpi++;
    }
    peak *pn = NULL;
    tpi++;
    if(tpi != peaks.end()) {
      pn = (*tpi);
    }

    peak *tp;
    peak *tn;
    tp = p->tp;
    tn = p->tn;

    int k1 = p->k;
    int k0 = tp->k;
    real kf0;
    if(k0 < k1-peakWidth) {
      k0 = k1-peakWidth;
      kf0 = 0;
    } else {
      kf0 = k0 > tp->x ? k0 - tp->x : tp->x - k0;
    }
    int k2 = tn->k;
    real kf2;
    if(k2 > k1+peakWidth) {
      k2 = k1+peakWidth;
      kf2 = 0;
    } else {
      kf2 = k2 > tn->x ? k2 - tn->x : tn->x - k2;
    }

    real m = p->m;
    real mdec = 0;

    if(k0 < tp->x) {
      mdec += (dec[k0]+dec[k0+1])*(1.0-kf0);
    } else {
      mdec += (dec[k0]+dec[k0+1]);
      mdec += (dec[k0-1]+dec[k0])*kf0;
    }
    
    if(k2 < tn->x) {
      mdec += (dec[k2-1]+dec[k2]);
      mdec += (dec[k2]+dec[k2+1])*kf2;
    } else {
      mdec += (dec[k2-1]+dec[k2])*(1.0-kf2);
    }
    
    for(int k=k0+1;k<k2-1;k++) {
      mdec += (dec[k]+dec[k+1]);
    }

    mdec *= 0.5;

    if(mdec > maxDecRatio*m) {
      peaks.erase(eraseMe);
      m -= mdec;
      if(pp && pn) {
	real d = square(pn->x - p->x) + square(pp->x - p->x);
	pp->m += m*square(pn->x - p->x)/d;
	pn->m += m*square(pp->x - p->x)/d;
      } else if(pp) {
	pp->m += m;
      } else if(pn) {
	pn->m += m;
      }
      pa->destroy(p);
      continue;
    } else {
      m -= mdec;
      p->m = m;
    }
  }
}

void sms :: extractTrackPoints(grain *g, 
			       real *mag0, real *mag1, real *mag2,
			       long currtime,
			       TrackPointListBuffer *trackPointListBuffer,
			       unsigned short h1,
			       int c)
{
  real thmin = square(peakThresh)*magmax;
  list<peak*> peaks;
  list<peak*> troughs1;
  list<peak*> troughs2;

  peak *t0 = pa->create();
  t0->k = 1;
  t0->x = 1;
  t0->y = mag1[0];
  t0->y2 = mag2[0];
  t0->tp = NULL;
  t0->tp2 = NULL;
  t0->tn = NULL;
  t0->tn2 = NULL;

  peak *t1 = pa->create();
  t1->k = Nover2-1;
  t1->x = Nover2-1;
  t1->y = mag1[Nover2-1];
  t1->y2 = mag2[Nover2-1];
  t1->tp = NULL;
  t1->tp2 = NULL;
  t1->tn = NULL;
  t1->tn2 = NULL;

  troughs2.push_back(t0);

  int k00=0;
  int k01=Nover2-1;
  int k10=0;
  int k11=Nover2-1;
  int kp = -1;

  for(int k=1;k<Nover2-1;k++) {
    if( (mag2[k] > thmin)
	&&
	(mag2[k] > mag2[k-1])
	&&
	(mag2[k] >= mag2[k+1])
	) {
      kp = k;
      if(k>=kStart-peakWidth && k<=kEnd+peakWidth) {
	peak *p = makePeak(mag1,mag2,k);
	p->tn2 = t1;
	if(!troughs1.empty() && troughs1.back()->tn == NULL) {
	  p->tp = troughs1.back();
	  troughs1.back()->tn = p;
	}
	if(!troughs2.empty() && troughs2.back()->tn2 == NULL) {
	  p->tp2 = troughs2.back();
	  troughs2.back()->tn2 = p;
	}
	peaks.push_back(p);
      }
    } else if((mag2[k] <= mag2[k-1])
	      &&
	      (mag2[k] <= mag2[k+1])
	      ) {
      peak *p = makePeak(mag1,mag2,k);
      troughs2.push_back(p);
      if(!peaks.empty()) { 
	peaks.back()->tn2 = p;
	p->tp2 = peaks.back();
      }
    }
    if((mag0[k] <= mag0[k-1])
       &&
       (mag0[k] <= mag0[k+1])
       ) {   
      if(peaks.empty())
	k00 = k;
      if(kp <= kEnd)
	k01 = k;
    }
    if((mag1[k] <= mag1[k-1])
       &&
       (mag1[k] <= mag1[k+1])
       ) {      
      if(peaks.empty())
	k10 = k;
      if(kp <= kEnd)
	k11 = k;
      peak *p = makePeak(mag1,mag1,k);
      troughs1.push_back(p);
      if(!peaks.empty() && peaks.back()->tn == NULL) {
	peaks.back()->tn = p;
	p->tp = peaks.back();
      }
    }
  }
  real m0=0.0f;
  real m1=0.0f;
  for(int k=k00;k<=k01;k++) {
    m0 += mag0[k];
    m1 += mag1[k];
  }
  real f = (m1==0.0f)?1.0f:min(1.0f,m0/m1);

  adjustPeaks(peaks,
	      mag0,
	      mag1,
	      mag2,
	      dec,
	      peakWidth);

  tplist *trackPoints = new tplist;
  for(list<peak*>::iterator pp=peaks.begin();
      pp!=peaks.end();
      pp++) {

    peak *p = (*pp);
    if(p->x>=kStart-0.5 && p->x<=kEnd+0.5 && p->m>0) {
      real y = sqrt(f*p->m*mNorm);
      trackpoint *tp = new trackpoint(g,p->x,y,N,M,h1,currtime);
      trackPoints->push_back(tp);
    }
  }

  pa->destroyAll();
#ifdef MULTITHREADED
  pthread_mutex_lock(&tplbMutex[c]);
#endif
  trackPointListBuffer->write(trackPoints);
#ifdef MULTITHREADED
  pthread_mutex_unlock(&tplbMutex[c]);
#endif
}

}
