ftp.nice.ch/pub/next/unix/audio/cmusic.bs.N.tar.gz#/src/sig/envanal.c

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

/* cc envanal.c -lsf -lfrm -lm */

/*
 * envanal - data reduction by hierarchical syntactic function analysis.
 * 
 * Author: Gareth Loy
 *
 * This program is based on Strawn's algorithm of syntactic analysis
 * [John Strawn, "Approximation and Syntactic Analysis of Amplitude
 * and Frequency Functions for Digital Sound, CMJ, V4, #3].
 * 
 * It implements a two level grammar for a data reduction by
 * the recursive recognition of line segment features of
 * an input waveform.  It produces on the standard output either a 
 * summary of these features, a plot of the resulting data-reduced function,
 * or the floating point binary representation of that function.
 */
/*
 * In addition, a peak-reading algorithm has been added that causes the
 * analysis to proceed along the tops of the input segments.
 */

#include<stdio.h>
#include <math.h>
#include <carl/frm.h>
#include <carl/defaults.h>
#include <carl/carl.h>

#define UP 1
#define LL 0
#define DN -1

struct seg {
	int dir;
	long lbeg;
	float vbeg;
	long lend;
	float vend;
	} *sb1, *sb2;

#define SEG 1
#define PLOT 2
#define FLOAT 4
#define GEN 8
#define ENVBUG 1
#define PEAKBUG 2
int bug = 0, verbose = 0, otty, omode = SEG;
long scnt = 0, scnt1, scnt2;
int ybrk = 0, dbrk = 0, xbrk = 0, ycat = 0, dcat = 0;
float avesegl = 0, avesegv = 0; 
int prate = 1, peak = 0;

main(argc, argv)
	char **argv;
{
	struct seg *s, *getmseg();
	char ch;
	float sfexpr();
	float srate = FDHISR;
	float durThresh = .01, yThresh = .01, sumThresh = .1, pThresh = .01;
	float DurThresh = .1, YThresh = .2, SumThresh = .4;
	int i = 0, j, k,  passone = 0, passtwo = 1;
	long nsegs = 8*BUFSIZ;

/*
 * durThresh - set time-domain length >= which an input segment causes a break
 * 	(.01).
 * yThresh - set amplitude difference > which an input segment will cause a 
 * 	break (.01).
 * sumThresh - set maximum size to which a segment can grow before causing a 
 * 	break (.1).
 * DurThresh - pass 2 version of durThresh (.1).
 * YThresh - pass 2 version of YThresh. (.2)
 * SumThresh - pass 2 version of SumThresh (.4).
 * nsegs - max. # of segments expected (8K segments).
 * passone, passtwo - output results of this pass only (pass 2)
 * omode - output format: GEN, FLOAT, PLOT (tty:SEG, pipe:FLOAT)
 */

	otty = isatty(1);
	while ((ch = crack(argv,argv,"hv12gfP|b|p|d|y|s|D|Y|S|R|n|", 0))!= NULL)
		{
		switch(ch)
			{
			case 'd': durThresh = sfexpr(arg_option, 1.0); break;
			case 'y': yThresh = sfexpr(arg_option, 1.0); break;
			case 's': sumThresh = sfexpr(arg_option, 1.0); break;
			case 'D': DurThresh = sfexpr(arg_option, 1.0); break;
			case 'Y': YThresh = sfexpr(arg_option, 1.0); break;
			case 'S': SumThresh = sfexpr(arg_option, 1.0); break;
			case 'R': srate = sfexpr(arg_option, 1.0); break;
			case 'n': nsegs = sfexpr(arg_option, 1.0); break;
			case 'b': bug = sfexpr(arg_option, 1.0); break;
			case 'v': verbose++; break;
			case '1': passone++; passtwo = 0; break;
			case '2': passone++; passtwo++; break;
			case 'g': omode = GEN; break;
			case 'f': omode = FLOAT; 
				  if (otty) { fprintf(stderr,
				  "output must be a file or pipe for -f\n");
				  exit(1); }
				  break;
			case 'p': omode = PLOT; 
				  prate = sfexpr(arg_option, 1.0);
				  if (prate == 0) prate = 1;
				  break;
			case 'P': peak++; 
				  pThresh = sfexpr(arg_option, 1.0); 
				  if (pThresh==0) pThresh=.01;
				  break;
			case 'h': 
			case EOF:
			default:  envanalhelp();
			}
		}

	sb1 = (struct seg *) malloc(sizeof(struct seg)*nsegs);
	durThresh *= srate;	/* convt to samples */
	DurThresh *= srate;
	sumThresh *= srate;
	SumThresh *= srate;
	pThresh *= srate;

	if (verbose)
		fprintf(stderr, 
			"pass one: durThresh=%f yThresh=%f sumThresh=%f\n",
			durThresh, yThresh, sumThresh);

	while ((s = getmseg()) != NULL)
		{
		i = envanal(s, sb1, i, durThresh, yThresh, sumThresh);
		if (i >= nsegs)
			{
			fprintf(stderr, "ran out of pass1 seg storage!\n");
			fprintf(stderr, "find out about the -n flag\n");
			}
		}

	if (passone && bug == PEAKBUG)
		putout(sb1, i, srate);

	if (peak)
		i = pique(sb1, i, pThresh);

	if (passone)
		putout(sb1, i, srate);

	if (verbose)
	    {
	    fprintf(stderr, "pass one:\n");
	    fprintf(stderr, "\tnsamps=%d\tnsegs=%d\tcompression=%f\n", scnt, i, 
		    scnt*1.0/(i==0?1:i));
	    for (k = 0; k < i; k++)
		{
		avesegl += sb1[k].lend - sb1[k].lbeg;
		avesegv += fabs(sb1[k].vend - sb1[k].vbeg);
		}
	    fprintf(stderr, "\tave.seg.len=%f\tave.seg.delta=%f\n",
		    avesegl/(i==0?1:i), avesegv/(i==0?1:i));
	    fprintf(stderr, "\tybrk=%d\tdbrk=%d\txbrk=%d\t", ybrk, dbrk, xbrk);
	    fprintf(stderr, "ycat=%d\tdcat=%d\n\n", ycat, dcat);
	    }
	scnt1 = scnt;
	scnt = avesegl = avesegv = ybrk = dbrk = xbrk = ycat = dcat = 0;
	sb2 = (struct seg *) malloc(sizeof(struct seg)*i+1);

	if (verbose)
		fprintf(stderr, 
			"pass two: DurThresh=%f YThresh=%f SumThresh=%f\n",
			DurThresh, YThresh, SumThresh);

	for (j = k = 0; k <= i; k++)
		j = envanal(&sb1[k], sb2, j, DurThresh, YThresh, SumThresh);

	if (passtwo && bug == PEAKBUG)
		putout(sb2, j, srate);

	if (peak)
		j = pique(sb2, j, pThresh);

	if (passtwo)
		putout(sb2, j, srate);
	scnt2 = scnt;

	if (verbose)
	    {
	    fprintf(stderr, "pass two:\n");
	    fprintf(stderr, "\tnsamps=%d\tnsegs=%d\tcompression=%f\n", scnt, j, 
		    scnt*1.0/(j==0?1:j));
	    for (k = 0; k < i; k++)
		{
		avesegl += sb2[k].lend - sb2[k].lbeg;
		avesegv += fabs(sb2[k].vend - sb2[k].vbeg);
		}
	    fprintf(stderr, "\tave.seg.len=%f\tave.seg.delta=%f\n",
		    avesegl/(j==0?1:j), avesegv/(j==0?1:j));
	    fprintf(stderr, "\tybrk=%d\tdbrk=%d\txbrk=%d\t", ybrk, dbrk, xbrk);
	    fprintf(stderr, "ycat=%d\tdcat=%d\n", ycat, dcat);
	    fprintf(stderr, "overall compression=%f\n", scnt1*1.0/(j==0?1:j));
	    }

	if (!otty) flushfloat();
	}


struct seg *getmseg()
{
	static struct seg tseg;
	float input;
	static float last;
	static int first;
	static long cnt;

	if (!first) { first++; if (getfloat(&last) <= 0) return(NULL); }
	if (getfloat(&input) <= 0) return(NULL);

	tseg.lbeg = cnt++;
	tseg.vbeg = last;
	tseg.lend = cnt;
	tseg.vend = input;
	if (tseg.vend > tseg.vbeg) tseg.dir = UP;
	else
	if (tseg.vend == tseg.vbeg) tseg.dir = LL;
	else
	if (tseg.vend < tseg.vbeg) tseg.dir = DN;

	last = input;
	return(&tseg);
	}

envanalhelp()
{
	fprintf(stderr, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
	    "usage: envanal [flags] < floatsams > floatsams\n",
	    "input must be a file or pipe\n",
	    " flags:\n",
	    " dN = set pass 1 dur. thresh. to N (.01)\n",
	    " yN = set pass 1 amp thresh. to N (.01)\n",
	    " sN = set pass 1 max. seg. dur. thresh. to N (.1)\n",
	    " DN = set pass 2 dur. thresh. to N (.1)\n",
	    " YN = set pass 2 amp thresh. to N (.2)\n",
	    " SN = set pass 2 max. seg. dur. thresh. to N (.4)\n",
	    " RN = set sampling rate to R (48K)\n",
	    " nN = set est. # segs in pass 1 (16K)\n",
	    " 1  = produce pass one only (pass 2 only)\n",
	    " 2  = produce both passes one and two (2 only)\n",
	    " v = verbose\n",
	    " b = bug\n"
	    );
	exit(1);
	}
	
	
envanal(s, sb, i, durT, yT, sumT)
	struct seg *s, *sb; float durT, yT, sumT; int i;
{
	float segx, segy, segm; 
	int segd;
	register float ob, oe, ne;

	segm = s->lend - s->lbeg;
	segx = (sb[i].lend - sb[i].lbeg) + segm;
	ob = sb[i].vbeg;
	oe = sb[i].vend;
	ne = s->vend;
	segy = ne - oe;
	segy = segy < 0 ? -segy : segy;
	segd = sb[i].dir == s->dir ? 1 : 0;
	if (scnt == 0) segd = 1;
	if (bug == ENVBUG) fprintf(stderr, 
	    "mseg %d:%d - %d\t%f - %f\t%2d\n",
	    scnt, s->lbeg, s->lend, s->vbeg, s->vend,
	    s->dir);
	if (bug == ENVBUG) 
		fprintf(stderr, "(y=%f <= yT=%f || d=%d) && x=%f <= sT=%f",
		segy, yT, segd, segx, sumT);

	if (bug == ENVBUG) if (segy > yT) fprintf(stderr, "*");
	if (segm <= durT && (segy <= yT || segd) && segx <= sumT)
		{	/* concatenate */
		if (bug == ENVBUG) 
			{
			fprintf(stderr, "\tc:");
			if (segy <= yT) fprintf(stderr, "y\n");
			else fprintf(stderr, "d\n");
			}
		if (segy <= yT) ycat++;
		else	dcat++;
		/* extend current macroSeg */
		if (scnt == 0) sb[i].vbeg = s->vbeg;
		sb[i].lend += s->lend - s->lbeg;
		sb[i].vend = s->vend;
		if (bug == ENVBUG) fprintf(stderr, 
		    "xMseg %d:%d - %d\t%f - %f\t%2d\n",
		    i, sb[i].lbeg, sb[i].lend, sb[i].vbeg, sb[i].vend,
		    sb[i].dir);
		}
	else		/* new seg */
		{	/* start new macroSeg with current microSeg */
		if (bug == ENVBUG) fprintf(stderr, "\tN:");
		if (segx > sumT) 
			{ xbrk++; if (bug == ENVBUG) fprintf(stderr, "x\n"); }
		else
		if (segy > yT)
			{
			ybrk++; 
			if (bug == ENVBUG) fprintf(stderr, "y");
			if (!segd) { dbrk++; if (bug == ENVBUG) fprintf(stderr, "d"); }
			if (bug == ENVBUG) fprintf(stderr, "\n");
			}
		/* complete old macroSeg */
		if (bug == ENVBUG) fprintf(stderr, 
		    "oMseg %d:%d - %d\t%f - %f\t%2d\n",
		    i, sb[i].lbeg, sb[i].lend, sb[i].vbeg, sb[i].vend,
		    sb[i].dir);
		/* start new macroSeg */
		i++;
		sb[i].lbeg = s->lbeg;
		sb[i].vbeg = s->vbeg;
		sb[i].lend = s->lend;
		sb[i].vend = s->vend;
		sb[i].dir = s->dir;
		if (bug == ENVBUG) fprintf(stderr, 
		    "nMseg %d:%d - %d\t%f - %f\t%2d\n",
		    i, sb[i].lbeg, sb[i].lend, sb[i].vbeg, sb[i].vend,
		    sb[i].dir);
		}
	scnt++;
	return(i);
	}

putout(sb, n, srate)
	struct seg *sb; int n; float srate;
{
	register i, j;
	if (omode == SEG)
	    for (i = 0; i <= n; i++)
		printf(
		"%d:\tx: %dS(%6.3fs)<==>%dS(%6.3fs)\ty: %6.5f<==>%6.5f\t%2d\n",
		    i, sb[i].lbeg, sb[i].lbeg/srate, 
		    sb[i].lend, sb[i].lend/srate, 
		    sb[i].vbeg, sb[i].vend, sb[i].dir);
	else
	if (omode & (PLOT | FLOAT))
	    {
	    static int cnt;
	    char cseg, hseg;
	    register float inc, div;
	    float output;
	    for ( cnt = i = 0; i <= n; i++ )
		{
		div = (sb[i].lend-sb[i].lbeg);
		inc = (sb[i].vend-sb[i].vbeg)/div;
		output = sb[i].vbeg;
		cseg = i % 2 ? '*' :'#';
		for (j = sb[i].lbeg; j < sb[i].lend; j++, cnt++, output += inc) 
		    if (omode == FLOAT) 
			putfloat(&output);
		    else 
		    if (!(j % prate)) 
			plotline(output, cnt, cseg, '-');
		}
	    if (omode == FLOAT) putfloat(&sb[n].vend);
	    else plotline(sb[n].vend, cnt, cseg, '-');
	    }
	else
	if (omode == GEN)
	    {
	    printf("gen1 -L1024 ");
	    for (i = 0; i <= n; i++)
		printf("%f %f ", sb[i].lbeg/srate, sb[i].vbeg);
	    printf("%f %f;\n", sb[i-1].lend/srate, sb[i-1].vend);
	    }
	}
	
plotline(input, n, capital, fill)
	float input; int n; char capital, fill;
{
	register char c;
	int pos, i;
	float min = 0,max = 1;

	printf("%6d",n);
	c = capital;
	if(input < min){c = '<'; input = min;}
	if(input > max) {c = '>'; input = max;}
	pos = (input-min)/(max-min) * 70.0 + 0.5;
	if(pos <= 35){
	    for(i = 0; i < pos-1; i++) putchar(fill);
	    putchar(c);
	    if(pos != 35){
		if(pos==0)pos=1;
		for(i = pos; i<34; i++) putchar(' ');
		putchar('|');
	    }
	    putchar('\n');
	} else { 
	    for(i=0;i<34;i++) putchar(fill);
	    putchar('|');
	    for(i=0; i<pos-35; i++)putchar(fill);
	    putchar(c);
	    putchar('\n');
	}
    }

pique(sb, n, pT)
	struct seg *sb; int n; float pT;
{
	float hiend();
	register int i, j, oldj, k, maxl;

	for (i = 0, oldj = j = 1; j < n; j++)
		{
		if (sb[j].dir != UP) 
			continue;
		else
			if (hiend(sb,j) < hiend(sb,i) && 
				sb[j].lend - sb[i].lbeg <= pT)
					continue;
		if (sb[j].lend - sb[i].lbeg > pT)
			{
			register float maxv = 0;
			for (maxl = k = oldj+1; k < j; k++)
				if (hiend(sb,k) > maxv)
					{
					maxv = hiend(sb,k);
					maxl = k;
					}
			j = maxl;
			}
		i = gulp(sb, i, j);
		oldj = j;
		}

	if (sb[i].lend != sb[j].lend)	/* one left over */
		{
		i++;
		sb[i].lbeg = sb[i-1].lend;
		sb[i].vbeg = sb[i-1].vend;
		sb[i].lend = sb[j].lend;
		sb[i].vend = sb[j].vend;
		}
	return(i);
	}

float hiend(sb, x)
	struct seg *sb; int x;
{
	return(sb[x].vend > sb[x].vbeg ? sb[x].vend : sb[x].vbeg);
	}

gulp(sb, i, j)
	struct seg *sb; int i,j;
{
	register float dir;
	i++;
	dir = sb[j].vend - sb[i].vbeg;
	if (dir != 0) sb[i].dir = dir > 0 ? UP : DN;
	else sb[i].dir = 0;
	sb[i].lbeg = sb[i-1].lend;
	sb[i].lend = sb[j].lend;
	sb[i].vbeg = sb[i-1].vend;
	sb[i].vend = sb[j].vend;
	return(i);
	}

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