#include < libc.h> 
#include < iostream.h> 
#include < fstream.h> 
#include < stdio.h> 

#define SRATE 8192
#define DOUBLE_TO_SHORT(x) ((int)((x)*32768.0))

#define MAX(x,y) (x> y)?x:y

typedef struct _DelayLine {
    short *data;
    int length;
    short *pointer;
    short *end;
} DelayLine;

static DelayLine *initDelayLine(int len) {
    DelayLine *dl = (DelayLine *)calloc(len, sizeof(DelayLine));
    dl-> length = len;
    if (len >  0)
        dl-> data = (short *)calloc(len, len * sizeof(short));
    else
        dl-> data = 0;
    dl-> pointer = dl-> data;
    dl-> end = dl-> data + len - 1;
    return dl;
}

static void freeDelayLine(DelayLine *dl) {
    if (dl && dl-> data)
                free(dl-> data);
    dl-> data = 0;
    free(dl);
}

inline static void setDelayLine(DelayLine *dl, double *values, double scale) {
    int i;
    for (i=0; i< dl-> length; i++)
        dl-> data[i] = DOUBLE_TO_SHORT(scale * values[i]);
}

/* lg_dl_update(dl, insamp);
 * Places "nut-reflected" sample from upper delay-line into
 * current lower delay-line pointer location (which represents
 * x = 0 position).  The pointer is then incremented (i.e. the
 * wave travels one sample to the left), turning the previous
 * position into an "effective" x = L position for the next
 * iteration.
 */
static inline void lg_dl_update(DelayLine *dl, short insamp) {
    register short *ptr = dl-> pointer;
    *ptr = insamp;
        ptr++;
    if (ptr >  dl-> end)
        ptr = dl-> data;
    dl-> pointer = ptr;
}

/* rg_dl_update(dl, insamp);
 * Decrements current upper delay-line pointer position (i.e.
 * the wave travels one sample to the right), moving it to the
 * "effective" x = 0 position for the next iteration.  The
 * "bridge-reflected" sample from lower delay-line is then placed
 * into this position.
 */
static inline void rg_dl_update(DelayLine *dl, short insamp) {
    register short *ptr = dl-> pointer;    
        ptr--;
    if (ptr <  dl-> data)
        ptr = dl-> end;
        *ptr = insamp;
    dl-> pointer = ptr;
}

/* dl_access(dl, position);
 * Returns sample "position" samples into delay-line's past.
 * Position "0" points to the most recently inserted sample.
 */
static inline short dl_access(DelayLine *dl, int position) {
    short *outloc = dl-> pointer + position;
    while (outloc <  dl-> data)
        outloc += dl-> length;
    while (outloc >  dl-> end)
        outloc -= dl-> length;
    return *outloc;
}

/*
 *  Right-going delay line:
 *  --> ----> ----> --- 
 *  x=0
 *  (pointer)
 *  Left-going delay line:
 *  --< ----< ----< --- 
 *  x=0
 *  (pointer)
 */

/* rg_dl_access(dl, position);
 * Returns spatial sample at location "position", where position zero
 * is equal to the current upper delay-line pointer position (x = 0).
 * In a right-going delay-line, position increases to the right, and
 * delay increases to the right =>  left = past and right = future.
 */
static inline short rg_dl_access(DelayLine *dl, int position) {
    return dl_access(dl, position);
}

/* lg_dl_access(dl, position);
 * Returns spatial sample at location "position", where position zero
 * is equal to the current lower delay-line pointer position (x = 0).
 * In a left-going delay-line, position increases to the right, and
 * delay DEcreases to the right =>  left = future and right = past.
 */
static inline short lg_dl_access(DelayLine *dl, int position) {
    return dl_access(dl, position);
}

static DelayLine *upper_rail,*lower_rail;


static inline int initString(double amplitude, double pitch,
                             double pick, double pickup) {
    int i, rail_length = SRATE/pitch/2 + 1;
        /* 
         * Round pick position to nearest spatial sample.
         * A pick position at x = 0 is not allowed. 
         */
    int pickSample = MAX(rail_length * pick, 1); 
        double upslope = amplitude/pickSample;
    double downslope = amplitude/(rail_length - pickSample - 1);
    double initial_shape[rail_length];

    upper_rail = initDelayLine(rail_length);
    lower_rail = initDelayLine(rail_length);

#ifdef DEBUG
    initial_shape[pickSample] = 1;
#else
    for (i = 0; i <  pickSample; i++)
        initial_shape[i] = upslope * i;
    for (i = pickSample; i <  rail_length; i++)
        initial_shape[i] = downslope * (rail_length - 1 - i);
#endif

    /*
     * Initial conditions for the ideal plucked string.
     * "Past history" is measured backward from the end of the array.
     */
    setDelayLine(lower_rail, initial_shape, 0.5);
    setDelayLine(upper_rail, initial_shape, 0.5);

    return pickup * rail_length;
}

static inline void freeString(void) {
    freeDelayLine(upper_rail);
    freeDelayLine(lower_rail);
}

static inline short bridgeReflection(int insamp) {
    static short state = 0; /* filter memory */
    /* Implement a one-pole lowpass with feedback coefficient = 0.5 */
    /* outsamp = 0.5 * outsamp + 0.5 * insamp */
    //short outsamp = (state > >  1) + (insamp > >  1);
    short outsamp = .1* outsamp + .9 * insamp;
    state = outsamp;
    return outsamp;
}

static inline short nextStringSample(int pickup_loc) {
    short yp0,ym0,ypM,ymM;
    short outsamp, outsamp1;

    /* Output at pickup location */
    outsamp  = rg_dl_access(upper_rail, pickup_loc);
    outsamp1 = lg_dl_access(lower_rail, pickup_loc);
        outsamp += outsamp1;

    ym0 = lg_dl_access(lower_rail, 1);     /* Sample traveling into "bridge" */
    ypM = rg_dl_access(upper_rail, upper_rail-> length - 2); /* Sample to "nut" */

    ymM = -ypM;                    /* Inverting reflection at rigid nut */
    yp0 = -bridgeReflection(ym0);  /* Reflection at yielding bridge */

    /* String state update */
    rg_dl_update(upper_rail, yp0); /* Decrement pointer and then update */
    lg_dl_update(lower_rail, ymM); /* Update and then increment pointer */

    return outsamp;
}

/* Utility for writing a mono sound to a sound file on a NeXT machine */
//#include < sound/sound.h> 
 static int writeSound(char *name, short *soundData, int sampleCount) {
   int i, err;
   short *data;
   // SNDSoundStruct *sound;
   //   SNDAlloc(&sound, sampleCount * sizeof(short), SND_FORMAT_LINEAR_16,
   //      SRATE,1,4);
   //   data = (short *) ((char *)sound + sound-> dataLocation);
   //   for (i = 0; i <  sampleCount; i++)
   //     data[i] = soundData[i];

   // ofstream output(name);
   FILE *fpt = fopen(name,"w");
   for(int i=0; i< sampleCount; i++) {
     //output > >  soundData[i] > >  endl;
     //printf("%i ",soundData[i]);
     fprintf(fpt,"%i ",soundData[i]);
   }

   fclose(fpt);
   
   return err;
 }

static void writeString(void) {
    int i, sampleCount = upper_rail-> length;
    short data[sampleCount];

    for (i = 0; i <  sampleCount; i++)
        data[i] = rg_dl_access(upper_rail,i);
    writeSound("upper.snd", data, sampleCount);
 
    for (i = 0; i <  sampleCount; i++)
        data[i] = lg_dl_access(lower_rail,i);
    writeSound("lower.snd", data, sampleCount);
  
    for (i = 0; i <  sampleCount; i++)
        data[i] = rg_dl_access(upper_rail,  i) + lg_dl_access(lower_rail, i);
   writeSound("string.raw", data, sampleCount);
}

void main (int argc, char *argv[]) {
    int i, sampleCount;
    short *data;
    double amp, duration, pitch, pick, pickup, writesample;
    int pickupSample;

    if (argc != 8) {
      cerr < <  "Usage: " < <  argv[0]
           < <  " amp(< 1.0) pitch(Hz) pickPosition(< 1.0) "
           < <   "pickupPosition(< 1.0) duration(sec) writeSamp out.snd\n";

      cerr < <  "example: %s .5 100 .1 .2 1 -1 test.snd" < <  endl;
      exit(1);
    }

    sscanf(argv[1],"%lf",&);
    sscanf(argv[2],"%lf",&pitch);
    sscanf(argv[3],"%lf",&pick);
    sscanf(argv[4],"%lf",&pickup);
    sscanf(argv[5],"%lf",&duration);
    sscanf(argv[6],"%lf",&writesample);

    sampleCount = duration * SRATE;
    cout < <  "samples: " < <  sampleCount < <  endl;
    //    data = (short *) malloc(sampleCount * sizeof(short));
    data = new short[sampleCount];
    pickupSample = initString(amp, pitch, pick, pickup);

    for (i = 0; i <  sampleCount; i++) {
      if (i == writesample) {
        printf("Writing string snapshot at sample %d\n",i);
        writeString();
      }
      data[i] = nextStringSample(pickupSample);
    }
    
    writeSound(argv[7], data, sampleCount);
    freeString();
    exit(0);
}