ftp.nice.ch/pub/next/unix/audio/fugue.s.tar.gz#/fugue/fugue.c

This is fugue.c in view mode; [Download] [Up]

/*
 * FILE: fugue.c
 *   BY: Christopher Lee Fraley (cf0v@spice.cs.cmu.edu)
 * DESC: Basic sound manipulation functions for an X-Lisp sound type.
 *
 * 1.0 (13-FEB-89) - Created from "sound.c".  Moved all of the sound
 *			manipulation functions here, leaving the primative
 *			constructors and desctructors in "sound.c", as well
 *			as a few other misc. primitives, such as s_copy(),
 *			s_flatten(), etc. (cf0v)
 * 1.1 (16-FEB-89) - I noticed I make the assumption that (int)double (i.e.
 *			type-casting a double into an integer) does the same
 *			as (int)(floor(double)).  That is, type casting must
 *			not round, but should just drop the fractional part.
 * 1.2 (25-FEB-89) - MidiToHz() added. (cf0v)
 * 1.3 (20-APR-89) - logicalTo() added. (cf0v)
 * 1.4 ( 5-MAY-89) - renamed MidiToHz() to step_to_hz(); added instrument key
 *			to s_osc(). (cf0v)
 *     (29-OCT-89) - added s_from, s_dur, s_to, s_logicalto (rbd)
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <math.h>
#include "xlisp.h"
#include "sound.h"
#include "fugue.h"

/* compute_phase -- given a phase in radians, a wavetable specified as
 *  the nominal pitch (in half steps), the table length, and the sample
 *  rate, compute the sample number corresponding to the phase.  This
 *  routine makes it easy to initialize the table pointer at the beginning
 *  of various oscillator implementations in Fugue.  Note that the table
 *  may represent several periods, in which case phase 2PI is not the same
 *  as 0.  Also note that the phase increment is also computed and returned
 *  through incr_ptr.
 */
double compute_phase(phase, key, n, srate, new_srate, freq, incr_ptr)
  double phase;  /* phase in radians */
  double key;    /* the semitone number of the table played at srate */
  long n;        /* number of samples */
  double srate;  /* the sample rate of the table */
  double new_srate;  /* sample rate of the result */
  double freq;   /* the desired frequency */
  double *incr_ptr; /* the sample increment */
{
    double period = 1.0 / step_to_hz(key);

    /* convert phase to sample units */
    phase = srate * period * (phase / (double) ANGLEBASE);
    /* phase is now in sample units; if phase is less than zero, then increase
       it by some number of sLength's to make it positive:
     */
    if (phase < 0)
        phase += (((int) ((-phase) / n)) + 1) * n;

    /* if phase is longer than the sample length, wrap it by subtracting the
       integer part of the division by sLength:
     */
    if (phase > n)
        phase -= ((int) (phase / n)) * n;

    /* Now figure the phase increment: to reproduce original pitch
       required incr = srate / new_srate.  To get the new frequency,
       scale by freq / nominal_freq = freq * period:
     */
    *incr_ptr = (srate / new_srate) * freq * period;
    return phase;
}


double step_to_hz(pitch)
  double pitch;
{
    return (16.351597830 * pow(2.0, pitch / 12.0));
}


double hz_to_step(hz)
  double hz;
{
    return 57.0 + (12 / log(2.0)) * (log(hz) - log(440.0));
}


double s_logicalTo(s)
SoundPtr s;
{
   return (s->logicalTo);
}

SoundPtr s_setLogicalTo(t, s)
double t;
SoundPtr s;
{
   s = s_copy(s);
   s->logicalTo = t;
   return (s);
}



float s_maxSample(s1)
SoundPtr s1;
{
   SDataType max;
   SDataPtr data, end;
   SamplePtr sample;

   switch (s1->tag)
      {
      case SUMNODES:
	 (void) s_flatten(s1);
	 /* FALL-THROUGH TO SAMPLES: */

      case SAMPLES:
	 sample = s1->ptr.sample;
	 data = sample->data + 1 + (int)(s1->from * sample->srate + 0.5);
	 data = MAX(data, sample->data);
	 end = sample->data + 1 + (int)(s1->to * sample->srate + 0.5);
	 end = MIN(end, sample->data + sample->length);
	 max = 0;
	 for (; data < end; data++)
	    max = MAX(max, ABS(*data));
	 return (max * s1->scale);

      case SILENCE:
      default:
	 break;
      }
   return (-1.0);
}



SoundPtr s_clip(s1, from, to)
SoundPtr s1;
double from, to;
{
   SoundPtr s;

   s = s_copy(s1);
   s->from = MAX(s1->from, from);
   s->to = MIN(s1->to, to);
   if (s->from >= s->to)
      s_silence(s);
   return (s);
}


SoundPtr s_lclip(s1, deltaFrom)
SoundPtr s1;
double deltaFrom;
{
   SoundPtr s;

   if (deltaFrom <= 0.0)
      return (s1);
   s = s_copy(s1);
   s->from += deltaFrom;
   if (s->from >= s->to)
      s_silence(s);
   return (s);
}


SoundPtr s_rclip(s1, deltaTo)
SoundPtr s1;
double deltaTo;
{
   SoundPtr s;

   if (deltaTo <= 0.0)
      return (s1);
   s = s_copy(s1);
   s->to -= deltaTo;
   if (s->from >= s->to)
      s_silence(s);
   return (s);
}



SoundPtr s_add(s1, s2)
SoundPtr s1, s2;
{
   SoundPtr s;
   int valid1, valid2;
   double beg, end, beg2, end2;

   valid1 = extent(s1, &beg, &end);
   valid2 = extent(s2, &beg2, &end2);
   beg = MIN(beg, beg2);
   end = MAX(end, end2);
   if (!valid1)
       return (s2);
   else if (!valid2)
       return (s1);

   s = s_create();
   s->from = beg;
   s->to = end;
   s->logicalTo = MAX(s1->logicalTo, s2->logicalTo);
   s->srate = MAX(s1->srate, s2->srate);
   s->tag = SUMNODES;
   s->ptr.node = n_create(s1);
   s->ptr.node->next = n_create(s2);
   return (s);
}



SoundPtr s_scale(s1, f)
SoundPtr s1;
double f;
{
   SoundPtr s;

   s = s_copy(s1);
   s->scale *= f;
   if (f == 0.0)
      s_silence(s);
   return (s);
}


SoundPtr s_shift(s1, f)
SoundPtr s1;
double f;
{
   SoundPtr s;

   s = s_copy(s1);
   s->shift += f;
   s->logicalTo += f;
   return (s);
}


SoundPtr s_stretch(s1, f)
SoundPtr s1;
double f;
{
   SoundPtr s;

   s = s_copy(s1);
   s->stretch *= f;
   s->logicalTo *= f;
   if (f == 0.0)
      s_silence(s);
   return (s);
}


float s_from(s)
SoundPtr s;
{
    return s->from;
}


float s_to(s)
SoundPtr s;
{
    return s->to;
}


float s_dur(s)
SoundPtr s;
{
    return s->to - s->from;
}


/* s_samples -- return samples in a lisp array */
/**/
LVAL s_samples(s, limit)
  SoundPtr s;
  int limit;
{
    int len;
    SDataPtr data;
    LVAL v;
    int i;
    int valid;
    double minimum, maximum;

    if (s->tag != SAMPLES) {
        xlerror("expected flattened samples", s);
    }
    valid = extent(s, &minimum, &maximum);
    if (!valid) return newvector(0);

    /* protect the pointer to the vector */
    xlsave1(v);

    len = (maximum - minimum) * s->srate + 0.5;
    if (len > limit) len = limit;
    data = locate_sample(s, minimum);
    v = newvector(len);
    for (i = 0; i < len; i++) {
        setelement(v, i, cvflonum(data[i]));
    }

    /* restore the stack */
    xlpop();
    return v;
}


/* show_indent -- indentation level for s_stats */
int show_indent = 0;


/* indent -- print spaces to indent by stat_indent */
int indent()
{
    int i;
    for (i = 0; i < show_indent; i++) printf("   ");
}


char *tag_name[] = {"SILENCE", "SAMPLES", "SUMNODES"};

/* s_show -- print sound structure for debugging */
/**/
SoundPtr s_show(s)
  SoundPtr s;
{
    indent();
    printf("{%x} tag:%s\n", s, tag_name[s->tag]);
    indent();
    printf("   from:%g  to:%g  shift:%g  stretch:%g\n",
	   s->from, s->to, s->shift, s->stretch);
    indent();
    printf("   logTo:%g  scale:%g  srate:%g  refCount:%d\n",
	   s->logicalTo, s->scale, s->srate, s->refCount);
    if (s->tag == SAMPLES) {
        int i, len;
        SDataPtr data;

        len = s->ptr.sample->length;
        data = s->ptr.sample->data;
	indent();
	printf("   sound: {%x}\n", s->ptr.sample);
	indent();
	printf("      refCount: %d  len:%d  srate:%g  data:{%x}\n", 
	       s->ptr.sample->refCount, len, s->ptr.sample->srate, data);
    } else if (s->tag == SUMNODES) {
        NodePtr n;
	show_indent++;
        for (n = s->ptr.node; n != NULL; n = n->next) {
	    indent();
	    printf("{%x}->\n", n);
	    s_show(n->sound);
        }
	show_indent--;
        putchar('\n');
    }
    return (s);
}


float s_srate(s)
  SoundPtr s;
{
    return s->srate;
}


SoundPtr s_stats(s)
  SoundPtr s;
{
    printf("{%x} tag:%s\n", s, tag_name[s->tag]);
    printf("   from:%g  to:%g  shift:%g  stretch:%g  logTo:%g\n",
	   s->from, s->to, s->shift, s->stretch, s->logicalTo);
    printf("   scale:%g  srate:%g  refCount:%d\n",
	   s->scale, s->srate, s->refCount);
    if (s->tag == SAMPLES) {
        int i, len;
        SDataPtr data;

        len = s->ptr.sample->length;
        data = s->ptr.sample->data;
	printf("   len:%d [", len);
        for (i = 1; i <= 6 && i < len; i++)
            printf("%g, ", data[i]);
        if (i < len) printf("..., ");
        i = MAX(i, len - 3);
        for (; i < len; i++)
            printf("%g, ", data[i]);
        printf("%g]\n", data[len]);
    }
    putchar('\n');
    return (s);
}


/* s_access -- lookup the value at a particular time point */
/**/
float s_access(s, t)
  SoundPtr s;   /* the sound */
  double t;     /* the time */
{
    double min_time, max_time;
    int valid;
    double exact_offset, frac;
    SDataPtr ptr;
    /* must be samples before we can access it */
    (void) s_flatten(s);
    if (s->tag == SILENCE) return 0.0;
    valid = extent(s, &min_time, &max_time);
    if (!valid || t < min_time || t > max_time) return 0.0;
    exact_offset = (t - s->shift) * s->srate;
    ptr = s->ptr.sample->data + 1 + (long) exact_offset;
    frac = exact_offset - floor(exact_offset);
    return ptr[0] * (1.0 - frac) + ptr[1] * frac;
}


SoundPtr s_apply(s1, sc, from, to, sh, str, srate)
  SoundPtr s1; /* the sound to be modified */
  double sc;   /* scale factor */
  double from; /* clip from */
  double to;   /* clip to */
  double sh;   /* shift by */
  double str;  /* stretch by */
  double srate;/* sample rate */
{
   SoundPtr s;

/*   printf(
     "s_apply: snd %x scale %g from %g to %g shift %g stretch %g srate %g\n",
	  s1, sc, from, to, sh, str, srate); */
   s = s_copy(s1);
   /* first scale samples */
   s->scale *= sc;

   /* then extract the period from start to stop */
   /* notice that since s->from and s->to are shifted by s-shift, we
    * have to shift from and to parameters by s->shift before clipping */
   s->from = MAX(s->from, from - s->shift);
   s->to = MIN(s->to, to - s->shift);

   /* shift the result by sh */
   s->shift += sh;

   /* and stretch by str */
   s->stretch *= str;

   /* shift and stretch s->logicalTo */
   s->logicalTo = (s->logicalTo + sh) * str;
   if (srate != 0.0)
      s->srate = srate;
   if ((s->scale == 0.0) || (s->to <= s->from) || (s->stretch == 0.0))
      s_silence(s);
   return (s);
}


SoundPtr s_mult(s1, s2)
  SoundPtr s1, s2;
{
    int len;
    SoundPtr s, temp;
    SamplePtr sample;
    SDataPtr data, data1, data2;
    double begin, end, srate;
    double min1, min2, max1, max2;
    int valid1, valid2;

    s = s_create();
    (void) s_flatten(s1);
    (void) s_flatten(s2);
    if ((s1->tag == SILENCE) || (s2->tag == SILENCE))
        return (s);

    srate = MAX(s1->srate, s2->srate);
    valid1 = extent(s1, &min1, &max1);
    valid2 = extent(s2, &min2, &max2);
    begin = MAX(min1, min2);
    end = MIN(max1, max2);
    len = (end - begin) * srate + 0.5;
    if ((len <= 1) || !valid1 || !valid2)
        return (s);

    data = sdata_create(len);
    sample = spl_create(data, len, srate);

    data++; /* skip over initial zero */
    s->to = end - begin;
    s->shift = begin;
    s->logicalTo = MIN(s1->logicalTo, s2->logicalTo);
    s->srate = srate;
    s->tag = SAMPLES;
    s->ptr.sample = sample;
    if (APPROX(s1->srate, s2->srate)) {
        data1 = locate_sample(s1, begin);
        data2 = locate_sample(s2, begin);
        for (; len != 0; len--)
	    *(data++) = *(data1++) * (*(data2++));
        return (s);
    } else {
        double step2, index2, d, f;
        int index1;

        if (s1->srate < s2->srate) {
	    temp = s1;
            s1 = s2;
            s2 = temp;
        }
        /* srate1 > srate2 */
        step2 = s2->srate / s1->srate;
	/* why did Chris add the 0.5 to only one of these? */
        index1 = 1 + (begin - s1->shift) * s1->srate + 0.5;
        index2 = 1 + (begin - s2->shift) * s2->srate;
	data1 = data + len;  /* use this as a termination marker */
        for (; data < data1; data++, index1++, index2 += step2) {
	    register int i2 = floor(index2);
	    d = index2 - i2;
	    f = 1.0 - d;
	    *data = s1->ptr.sample->data[index1] *
		  (f * s2->ptr.sample->data[i2] +
		   d * s2->ptr.sample->data[i2 + 1]);
	}
    return (s);
    }
}


SoundPtr sf_load(file, srate)
char *file;
double srate;
{
   int fin;
   SoundPtr s;
   SamplePtr sample;
   SDataPtr data;
   SFDataPtr inbuff, temp;
   char error[80];
   struct stat sbuff;
   unsigned int nbytes;
   int size;

   if (stat(file, &sbuff) < 0)
      xlabort(sprintf(error, "SF-LOAD: Cannot stat file '%s'", file));
   if (0 > (fin = open(file, 0)))
      xlabort(sprintf(error, "SF-LOAD: Cannot open file '%s'", file));
   s = s_create();
   nbytes = sbuff.st_size;
   if (NULL == (inbuff=(SFDataPtr)malloc(nbytes)))
      xlabort("SF-LOAD: insufficient memory - extern");
   /* fprintf(stderr, "inbuff=%x\n", inbuff); */
   if (nbytes != read(fin, (char *)inbuff, (int)nbytes))
      xlabort(sprintf(error, "SF-LOAD: error reading file '%s'", file));
   size = nbytes / sizeof(SFDataType);
   (void)close(fin);
   if (size <= 1)
      return (s);

   data = sdata_create(size);
   sample = spl_create(data, size, srate);
   s->to = s->logicalTo = (double) size / srate;
   s->srate = srate;
   s->tag = SAMPLES;
   s->ptr.sample = sample;

   temp = inbuff;
   data++; /* skip over initial zero */
   for (; size!=0; size--)
      *(data++) = *(inbuff++);
   free((char *)temp);
   return (s);
}



SoundPtr sf_save(s1, file)
SoundPtr s1;
char *file;
{
   double f;
   int fout;
   int i, len;
   SFDataPtr buff, ptr;
   SDataType max;
   SDataPtr data;
   char error[80];

   (void) s_flatten(s1);
   if (!(max = s_maxSample(s1)))
      {
      printf("WARNING: sf_save() did not save SILENCE\n");
      return (s1);
      }
   if (max != 0.0)
      f = 32767 / max;
   else
      f = 1.0;

   if (0 > (fout = creat(file, PERMS)))
      xlabort(sprintf(error, "SF-SAVE: Cannot create file '%s'", file));
   data = s1->ptr.sample->data + 1;  /* ignore initial zero */
   len = s1->ptr.sample->length;
   if (NULL == (buff = (SFDataPtr)malloc((unsigned)len * sizeof(SFDataType))))
      xlabort("SF-SAVE: Cannot malloc room for output buffer.");
   for (ptr=buff, i=0; i<len; i++)
      *(ptr++) = *(data++) * f + 0.5;
   (void)write(fout, (char *)buff, len*sizeof(SFDataType));
   (void)close(fout);
   free((char *)buff);
   return (s1);
}



SoundPtr s_env(srate, t1, t2, t3, t4, l1, l2, l3)
  float srate;              /* the sample rate */
  float t1, t2, t3, t4;   /* the duration of each phase */
  float l1, l2, l3;         /* the level of each phase endpoint */
{
    float t[5];  /* time in each phase of the envelope */
    float l[5];  /* level at each endpoint */
    float len[5];/* the length in samples of each phase */
    float time;
    unsigned int size; /* total length of the envelope */
    register double lvl;  /* envelope level */
    register double incr; /* envelope slope */
    register SDataPtr ptr; /* pointer to resulting signal samples */
    SDataPtr beg, end;
    SoundPtr s;
    int i;
    register int samp; /* sample counter */

    l[0] = 0.0;
    l[1] = l1;		t[1] = ABS(t1);
    l[2] = l2;		t[2] = ABS(t2);
    l[3] = l3;		t[3] = ABS(t3);
    l[4] = 0.0;		t[4] = ABS(t4);

    s = s_create();
    size = 0;
    /* compute the number of samples for each phase and the total */
    for (i=1; i<=4; i++)
        size += len[i] = (int)(srate * t[i]);
    if (size <= 1) return (s);

    beg = ptr = sdata_create(size);
    ptr++;  /* skip past initial zero */
    end = ptr + size - 1;

    for (i=1; i<=4; i++) {
        lvl = l[i - 1];
	incr = (l[i] - l[i - 1]) / len[i];
        for (samp = 0; samp < len[i]; samp++) {
	    if (ptr > end) {
	        fprintf(stderr, "Fatal error in s_env!");
	        exit(1);
	    }
	    *ptr++ = lvl;
	    lvl += incr;
	}
    }

   s->to = s->logicalTo = size / srate;
   s->srate = srate;
   s->tag = SAMPLES;
   s->ptr.sample = spl_create(beg, size, srate);
   return (s);
}



SoundPtr s_osc(s, sKey, srate, freq, duration, phase, oneShot)
  SoundPtr s;
  double sKey, srate, freq, duration, phase;
  int oneShot;
{
    SDataPtr src;			/* Ptr to sample data of s */
    int sLength;			/* Len of s in samples */
    double sPeriod;                     /* Period corresponding to sKey */
    SoundPtr newS;			/* The newly created sound */
    unsigned int size;			/* Size of return sample */
    SDataPtr ptr;			/* Ptr to return data */
    double dt;				/* dTime per sample */
    double d, f;			/* Interpolation variables */
    int fp;

    if (oneShot) {
        newS = s_copy(s);
        if ((newS->stretch == 0.0) || (newS->tag == SILENCE))
	    return (s_silence(newS));
        newS->to = MIN(newS->to, (duration / newS->stretch)-newS->shift);
        newS->logicalTo = duration;
        if (newS->from >= newS->to)
	    return (s_silence(newS));
        return (newS);
    }
    newS = s_create();
    s_flatten(s);
    src = s->ptr.sample->data;
    size = duration * srate;
    if (size <= 1) return (newS);
    ptr = sdata_create(size);
    newS->from = 0.0;
    newS->to = newS->logicalTo = floor(duration * srate + 0.5) / srate;
    newS->stretch = 1.0;
    newS->srate = srate;
    newS->tag = SAMPLES;
    newS->ptr.sample = spl_create(ptr, size, srate);

    sPeriod = 1.0 / step_to_hz(sKey); /* convert canon sKey to period */

    /* warning: this is WRONG! */
    sLength = s->ptr.sample->length;

    phase = compute_phase(phase, sKey, sLength, s->srate, srate, freq, &dt);

    ptr++;  /* skip past initial zero */
    while (size--) {
        if (phase >= sLength) phase -= sLength;
        fp = floor(phase);
        d = phase - fp;
        f = 1.0 - d;
	*ptr++ = (f * src[fp]) +
	    d * (fp + 1 == sLength ? (*src) : src[fp + 1]);
	phase += dt;
    }
#ifdef OLDCODE
      for (;size && (int)phase<sLength; size--)
	 {
	 d = phase - floor(phase);
	 f = 1.0 - d;
	 if ((int)phase == sLength-1)
	    *ptr++ = (f * (*(src+(int)phase)) + d * (*src));
	 else
	    *ptr++ = (f * (*(src+(int)phase)) +
		      d * (*(src+1+(int)phase)));
	 phase += dt;
	 }
      phase -= sLength;
      }
#endif
    return (newS);
}


/* s_pwl -- build a piece-wise linear envelope function */
/**/
SoundPtr s_pwl(srate, lis)
  float srate;   /* the sample rate */
  LVAL lis;      /* list of points: x1 y1 x2 y2 ... xn */
{
    float time;
    unsigned int size; /* total length of the envelope */
    register double lvl;  /* envelope level */
    register double incr; /* envelope slope */
    register SDataPtr ptr; /* pointer to resulting signal samples */
    double x, y;
    SDataPtr beg, end;
    SoundPtr s;
    int i;
    register int samp, dest; /* sample counter */
    LVAL lval;

    /* check validity first */
    lval = lis;
/*    printf("pwl arg check\n"); */
    while (TRUE) {
        LVAL flo;
        if (!consp(lval)) {
	    xlerror("s-pwl: time value missing from list", lis);
	}
        flo = car(lval);
        if (!floatp(flo) ||
	    ((x = getflonum(flo)) <= 0.0)) {
	    xlerror("s-pwl: bad time value", flo);
	}
/*	printf("   %g ", x); */
	lval = cdr(lval);
	if (null(lval)) {
/*	    printf("\n");*/
	    break;
	}
	if (!consp(lval)) {
	    xlerror("s-pwl: bad breakpoint list", lis);
	}
	flo = car(lval);
	if (!floatp(flo)) xlerror("s-pwl: non-float breakpoint value", flo);
/*	printf("%g\n", y = getflonum(flo));*/
	lval = cdr(lval);
    }

    s = s_create();
    size = (int) (srate * x);
    if (size <= 1) return (s);
    beg = ptr = sdata_create(size);
    ptr++; /* skip past initial zero */
    end = ptr + size - 1;

    lval = lis;
    lvl = 0.0;
    samp = 0;
    while (!null(lval)) {
        x = getflonum(car(lval));
	lval = cdr(lval);
	if (null(lval)) y = 0.0;
	else {
	    y = getflonum(car(lval));
	    lval = cdr(lval);
	}
	dest = (srate * x);
	if (dest > samp) incr = (y - lvl) / (dest - samp);
/*	printf("incr %g for %d\n", incr, dest - samp); */
	while (samp < dest) {
	    if (ptr > end) {
	        fprintf(stderr, "Fatal error in s_pwl");
		exit(1);
	    }
	    *ptr++ = lvl;
	    lvl += incr;
	    samp++;
        }
    }
    s->to = s->logicalTo = size / srate;
    s->srate = srate;
    s->tag = SAMPLES;
    s->ptr.sample = spl_create(beg, size, srate);

    return (s);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.