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

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

/*
 * FILE: sound.c
 *   BY: Christopher Lee Fraley (cf0v@spice.cs.cmu.edu)
 * DESC: file for i/f'ing C sound functions to LISP
 *
 * 1.0 (23-SEP-88) - created. (cf0v)
 * 1.1 (10-OCT-88) - modified data structure. (cf0v)
 * 2.0 (10-NOV-88) - moved srate to SoundType; changed critical members of
 *			SoundType to double; corrected flatten.  SoundType.to
 *			is now re-defined so SoundType.to is referenced from
 *			the begining of the sound instead of end of sound,
 *			(I.e. Normally, to==len/srate.) (cf0v)
 * 2.1 ( 1-FEB-89) - added s_osc(), s_env() functions. (cf0v)
 * 3.0 (13-FEB-89) - moved all sound manipulation functions to "fugue.c",
 *			leaving only those required by X-Lisp to implement
 *			a new type, and primitives such as s_create(),
 *			s_copy(), and s_flatten(). (cf0v)
 * 4.0 (11-MAY-89) - finally found malloc problems!  cvsound() now increments
 *			refCount, while the create functions create sounds
 *			with refCount set to zero. (cf0v)
 */

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

static int readint();
static void writeint();

xtype_desc	sound_desc;
static LVAL	a_sound;


/* The SOUND Type: */


int soundp(s)
LVAL s;
{
   return (exttypep(s, a_sound));
}


LVAL cvsound(s)
SoundPtr s;
{
   s->refCount += 1;
   return (cvextern(sound_desc, (unsigned char *) s));
}




void sample_free(sample)
SamplePtr sample;
{
   if (--(sample->refCount) == 0)
      {
      free((char *)sample->data);
      free((char *)sample);
      }
}

void nodes_free(node)
NodePtr node;
{
   NodePtr next;

   for (; node!=NULL; node=next)
      {
      next = node->next;
      sound_free(node->sound);
      free((char *)node);
      }
}


SoundPtr s_silence(s)
SoundPtr s;
{
   switch (s->tag)
      {
      case SILENCE:
	 break;
      case SAMPLES:
	 sample_free(s->ptr.sample);
	 break;
      case SUMNODES:
	 nodes_free(s->ptr.node);
	 break;
      }
   s->tag = SILENCE;
   return (s);
}


void sound_free(s)
SoundPtr s;
{
   if (--(s->refCount) > 0)
      return;
   free((char *)s_silence(s));
}


SDataPtr sdata_create(size)
int size;
{
   SDataPtr data;

   if (NULL == (data=(SDataPtr)malloc((unsigned)(size + 2) *
				      sizeof(SDataType))))
      xlerror("insufficient memory - sound.c:sdata_create()");
   /* two extra samples provide zeros for interpolation: */
   data[0] = 0.0;
   data[size + 1] = 0.0;
   return (data);
}



SoundPtr s_create()
{
   SoundPtr s;

   if (NULL == (s = (SoundPtr) malloc(sizeof(SoundType))))
      xlerror("insufficient memory - sound.c:s_create()");
   s->scale = 1.0;
   s->from = 0.0;
   s->to = 0.0;
   s->logicalTo = 0.0;
   s->shift = 0.0;
   s->stretch = 1.0;
   s->srate = SRATE;
   s->tag = SILENCE;
   s->refCount = 0;
   return (s);
}


NodePtr n_create(s1)
SoundPtr s1;
{
   NodePtr n;

   if (NULL == (n = (NodePtr) malloc(sizeof(NodeType))))
      xlerror("insufficient memory - sound.c:n_create()");
   n->sound = s1;
   s1->refCount += 1;
   n->next = NULL;
   return (n);
}


void n_add(n1, s1)
NodePtr n1;
SoundPtr s1;
{
   n1->next = n_create(s1);
}


SamplePtr spl_create(data, size, srate)
SDataPtr data;
unsigned int size;
float srate;
{
   SamplePtr spl;

   if (NULL == (spl = (SamplePtr) malloc(sizeof(SampleType))))
      xlerror("insufficient memory - sound.c:spl_create()");
   spl->refCount = 1;
   spl->length = size;
   spl->srate = srate;
   spl->data = data;
   return (spl);
}



SoundPtr s_copy(s1)
SoundPtr s1;
{
   NodePtr n, *dest;
   SoundPtr  s;

   s = s_create();
   s->scale = s1->scale;
   s->from = s1->from;
   s->to = s1->to;
   s->shift = s1->shift;
   s->stretch = s1->stretch;
   s->logicalTo = s1->logicalTo;
   s->srate = s1->srate;
   s->tag = s1->tag;
   switch (s1->tag)
      {
      case SILENCE:
	 break;
      case SAMPLES:
	 s->ptr.sample = s1->ptr.sample;
	 s->ptr.sample->refCount += 1;
	 break;
      case SUMNODES:
	 for (dest= &(s->ptr.node), n=s1->ptr.node;
	      n!=NULL; n=n->next, dest= &((*dest)->next))
	    *dest = n_create(n->sound);
	 break;
      default:
	 s->tag = SILENCE;
	 break;
      }
   return (s);
}


int extent(s1, begin, end)
SoundPtr s1;
double *begin, *end;
{
   *begin = (s1->from + s1->shift) * s1->stretch;
   *end = (s1->to + s1->shift) * s1->stretch;
   if ((*begin >= *end) || (s1->tag == SILENCE))
      return (FALSE);
   return (TRUE);
}


void s_extent(s1, begin, end)
SoundPtr s1;
double *begin, *end;
{
   if (!extent(s1, begin, end))
      *begin = *end = 0.0;
}




/*
 * Diagram for transform:
 *
 *	    xform args:
 *	   +----------+
 *	   | scale    |
 *	   | from, to |
 *	   | shift    |
 *	   | stretch  |					      destS
 *	   +----------+		 +-------------+	+----------------+
 *	        |		 | effScale    |	| 1.0		 |
 *	        |	===>	 | effFrom, To |  ===>	| 0.0, len/srate |
 *	        |		 | effShift    |	| destS->shift	 |
 *	        |		 | effStretch  |	| 1.0		 |
 *	 +--------------+	 +-------------+	+----------------+
 *	 | s1->scale    |		|			|
 *	 | s1->from, to |		V			V
 *	 | s1->shift    |	       ...		      SAMPLE
 *	 | s1->stretch  |
 *	 +--------------+
 *		|
 *		V
 *	     SOURCE
 *
 */


static void xform(destS, s1, scale, from, to, shift, stretch)
SoundPtr destS, s1;
double scale, from, to, shift, stretch;
{
   NodePtr node;
   double effScale, effFrom, effTo, effStretch, effShift, srcSrate, destSrate;
   double equivStretch, srcStep, srcIndex, srcIndexEnd, d, f;
   SDataPtr src, dest, srcEnd;
   int destEndIndex, destIndex;

   if (s1->tag == SILENCE)
      return;

   effScale = s1->scale * scale;
   effFrom = MAX(s1->from, from/s1->stretch - s1->shift);
   effTo = MIN(s1->to, to/s1->stretch - s1->shift);
   if (effTo <= effFrom)
      return;
   effShift = s1->shift + shift / s1->stretch;
   effStretch = stretch * s1->stretch;

   switch (s1->tag)
      {
      case SUMNODES:
	 for (node=s1->ptr.node; node!=NULL; node=node->next)
	    xform(destS, node->sound, effScale, effFrom, effTo, effShift,
		  effStretch);
	 return;

      case SAMPLES:
	 srcSrate = s1->ptr.sample->srate;
	 destSrate = destS->ptr.sample->srate;
	 equivStretch = effStretch * destSrate / srcSrate;
	 src = s1->ptr.sample->data;
	 dest = destS->ptr.sample->data;
	 destEndIndex = destS->ptr.sample->length;
	 destIndex = ((effFrom+effShift)*effStretch - destS->shift)
	             * destSrate + 0.5;
	 if (destIndex < 0)
	    destIndex = 0;
	 dest += destIndex + 1; /* skip initial zero */

	 if (APPROX(equivStretch, 1.0))
	    {				/* If no interpolation is needed: */
	    int srcIndex, srcEndIndex, temp;

	    srcIndex = effFrom * srcSrate + 0.5;
	    if (srcIndex < 0)
	       srcIndex = 0;
	    srcEndIndex = MIN((int)(effTo*srcSrate+0.5),
			      s1->ptr.sample->length);
	    temp = (srcEndIndex - srcIndex) - (destEndIndex - destIndex);
	    if (temp > 0)
	       srcEndIndex == temp;
	    srcEnd = src + srcEndIndex + 1; /* skip initial zero */
	    src += srcIndex + 1; /* skip initial zero */
	    for (; src < srcEnd; src++, dest++)
	       *dest += effScale * (*src);
	    return;
	    }
	 else
	    {
	    double srcStep, srcIndex, srcEndIndex;

/*	    printf("xform doing interpolation\n"); */
	    srcStep = 1.0 / equivStretch;
	    srcIndex = 1 + (effFrom * srcSrate); /* skip initial zero */
	    if (srcIndex < 1.0)
	       srcIndex = 1.0;
	    srcEndIndex = 1 + MIN(effTo * srcSrate,
				  (double)(s1->ptr.sample->length));
	    for( ; srcIndex < srcEndIndex; srcIndex += srcStep, dest++)
	       {
	       d = srcIndex - floor(srcIndex);
	       f = 1.0 - d;
	       *dest += effScale * (f * src[(int) srcIndex] +
				    d * src[((int) srcIndex) + 1]);
	       }
	    return;
	    }

      default:
	 return;					/* Invalid tag */

      }
}



SoundPtr s_flatten(s1)
SoundPtr s1;
{
   unsigned int len;
   double begin, end, srate;
   SamplePtr sample;
   SDataPtr data;
   SoundPtr s;

   switch (s1->tag)
      {
      case SILENCE:
         return (s1);
	 break;

      case SAMPLES:
	 if (APPROX(s1->stretch, 1.0)
	     && APPROX(s1->scale, 1.0)
	     && APPROXLT(s1->from, 0.0)
	     && APPROXLT((s1->ptr.sample->length-0.5)/s1->ptr.sample->srate,
		          s1->to)
	     && APPROX(s1->ptr.sample->srate, s1->srate))
	    return (s1);
	 /* FALL-THROUGH! */

      case SUMNODES:
	 if (!extent(s1, &begin, &end))
	    return (s_silence(s1));
	 break;

      default:
         return (s1);
      }

   s = s_copy(s1);
   s_silence(s1); /* free s1 of sound and/or addends */
   srate = s1->srate;
   len = (end - begin) * srate + 0.5;
   data = sdata_create(len);
   sample = spl_create(data, len, srate);
   s1->scale = 1.0;
   s1->from = 0.0;
   s1->to = len/srate;
   s1->shift = begin;
   s1->stretch = 1.0;
   s1->tag = SAMPLES;
   s1->ptr.sample = sample;
/*   printf("flatten calling xform\n"); */
   xform(s1, s, 1.0, (double)NEGINFINITY, (double)INFINITY, 0.0, 1.0);
   sound_free(s);
   return (s1);
}



SoundPtr s_compose(array, srate)
LVAL array;
double srate;
{
   SDataPtr data;
   SamplePtr sample;
   SoundPtr s;
   LVAL temp;
   int i, size;

   if (!vectorp(array))
      xlerror("Bad argument type; (s-compose VECTOR)");
   size = getsize(array);
   data = sdata_create(size);
   sample = spl_create(data, size, srate);
   s = s_create();
   s->to = s->logicalTo = (double)size / srate;
   s->srate = srate;
   s->tag = SAMPLES;
   s->ptr.sample = sample;
   for (i=0; i<size; i++, data++)
      {
      temp = getelement(array, i);
      if (floatp(temp))
	 *data = getflonum(temp);
      else if (fixp(temp))
	 *data = getfixnum(temp);
      else
	 xlerror("Bad argument type; s-compose VECTOR contains non-number");
      }
   return (s);
}


SoundPtr s_constant(val, length)
double val, length;
{
   SDataPtr data;
   SamplePtr sample;
   SoundPtr s;

   data = sdata_create(2);
   sample = spl_create(data, 2, 2.0/length);
   *data = val;
   *(data+1) = val;
   s = s_create();
   s->to = s->logicalTo = length;
   s->srate = 2.0 / length;
   s->tag = SAMPLES;
   s->ptr.sample = sample;
   return (s);
}



static void sound_print(fptr, s)
LVAL fptr;
SoundPtr s;
{
   char buf[100];
   double begin, end;

   if (s == NULL)
      {
      xlputstr(fptr, "{SOUND: silence[%g]}", s->logicalTo);
      return;
      }

   (void)sprintf(buf, "{SOUND: {%x} ", s);
   xlputstr(fptr, buf);
   switch (s->tag)
      {
      case SILENCE:
	 (void)sprintf(buf, " silence}");
	 break;
      case SAMPLES:
	 if (!extent(s, &begin, &end))
	    (void)sprintf(buf, " sample[silence|%g]}", s->logicalTo);
	 else
	    (void)sprintf(buf, " sample[%g:%g|%g]@%g}", begin, end,
			  s->logicalTo, s->srate);
	 break;
      case SUMNODES:
	 if (!extent(s, &begin, &end))
	    (void)sprintf(buf, " node[silence|%g]}", s->logicalTo);
	 else
	    (void)sprintf(buf, " node[%g:%g|%g]@%g}", begin, end,
			  s->logicalTo, s->srate);
	 break;
      default:
	 (void)sprintf(buf, " illegal[%d]}", s->tag);
      }
   xlputstr(fptr, buf);
}


static void sound_save(fp, s)
FILE *fp;
SoundPtr s;
{
   printf("sound_save called\n");
}


static unsigned char *sound_restore(fp)
FILE *fp;
{
   SoundPtr s;

   printf("sound_restore called\n");
   s = s_create();
   return ((unsigned char *)s);
}


void test_init()
{
   sound_desc = create_desc("SOUND", sound_free, sound_print, sound_save,
      sound_restore);
}


void test_symbols()
{
   a_sound = xlenter("SOUND");
}

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