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.