view tools/merge.c @ 0:5242703e91d3 tip

Initial checkin for AIM92 aimR8.2 (last updated May 1997).
author tomwalters
date Fri, 20 May 2011 15:19:45 +0100
parents
children
line wrap: on
line source
/*
    merge.c     Merge N streams onto stdout using given operator.
    -------

 Usage:  merge  [options]  file1  file2  ...  fileN

 For each vector of items (p1,p2,...,pN) read from file1, file2,...,fileN
 respectively write the result of a merging operation on the stdout.

 An input item is a number depending upon the type option.
 The output item has the same type as that selected for input.

 Internal processing is in floating point. A scale factor is included to
 avoid overflow when casting output data.
 If 16-bit over or under-flow occurs a warning is printed on the stderr.

 The `range' option sets the start and duration of the process.
 Its arguments are of the form: range=a-b (where start=a and duration=b-a+1)
			    or: range=a   (where start=a and duration=1    )
 The arguments can be in time units (ms, s) or samples (no units), and both
 "min" (start of file) and "max" (end of file) are recognised.
 Samples in a file are numbered 0,1,2,...,max.

 All files are interpreted as functions sampled with origin at the point in
 the file given by the first argument of the `range' option.
 The functions are assumed to be zero for all samples beyond those specified
 in the files.

 The `phase' option shifts the origin to introduce a phase difference between
 files. The `phase' option takes as argument a comma-separated list of
 arguments which must be the same length as the number of file arguments.
 Each argument can be in time units (ms, s) or samples (no units).
 A +ve phase advances the origin into the file.
 A -ve phase retards the origin. If a negative phase in combination with
 the start of the file as set by the `range' option results in a new origin
 outside the specified file, then the difference is padded with zeroes.

 The weights option takes as argument a comma-separated list of real numbers
 which must be the same length as the number of file arguments.
 These number are applied as weights to each item prior to the merging
 operation.

*/

#include <stdio.h>
#include <math.h>
#include "options.h"
#include "strmatch.h"
#include "units.h"

char applic[] = "Merge N streams onto stdout using given operator." ;
char usage[]  = "merge [options] file1 file2 ... fileN" ;

static char *helpstr,   *debugstr,  *sampstr,   *opstr      ;
static char *phstr,     *rangestr,  *scalestr,  *typestr    ;
static char *wtstr ;

static Options option[] = {
    {   "help"      ,   "off"       ,  &helpstr     ,   "help"                                  , DEBUG   },
    {   "debug"     ,   "off"       ,  &debugstr    ,   "debugging switch"                      , DEBUG   },
    {   "samplerate",   "20kHz"     ,  &sampstr     ,   "samplerate "                           , VAL     },
    {   "operator"  ,   "mean"      ,  &opstr       ,   "operator for merging"                  , VAL     },
    {   "phase"     ,   "off"       ,  &phstr       ,   "phase shift (phi1,...,phiN)"           , VAL     },
    {   "weights"   ,   "off"       ,  &wtstr       ,   "weight coefficients (w1,...,wN)"       , VAL     },
    {   "range"     ,   "0-max"     ,  &rangestr    ,   "inclusive time range for all files"    , VAL     },
    {   "scale"     ,   "1.0"       ,  &scalestr    ,   "scale factor for output"               , VAL     },
    {   "type"      ,   "short"     ,  &typestr     ,   "datatype"                              , VAL     },
   ( char * ) 0 } ;

char *operator[] = {
    "cat"      ,
    "add"      ,
    "subtract" ,
    "multiply" ,
    "divide"   ,
    "abs"      ,
    "max"      ,
    "min"      ,
    "mean"     ,
    "norm"     ,
   ( char * ) 0 } ;


#define Cat          0  /* Functions (index of operator list) */
#define Add          1
#define Subtract     2
#define Multiply     3
#define Divide       4
#define Abs          5
#define Max          6
#define Min          7
#define Mean         8
#define Norm         9

#define sq(p)           ((p)*(p))

int     samplerate ;
int     bytes      ;
int     type       ;    /* datatype index */
int     op         ;    /* operator index */
float   scale      ;


main(argc, argv)
int    argc;
char **argv;
{
    FILE  **fp ;
    float  *vec, *weight ;
    int    *origin ;
    char  **list ;
    int     i, j, n, morehelp() ;
    int     a, b ;
    float   writevec(), f, newscale = 1. ;

    if ( ( i = getopts( option, argc, argv ) ) == 0 || !isoff( helpstr ) )
	helpopts2( helpstr, argv[0], applic, usage, option, morehelp ) ;

    samplerate  = to_Hz( sampstr, 0 ) ;
    scale       = atof( scalestr ) ;
    op          = opindex( opstr ) ;

    if ( ( type = typeindex( typestr ) ) < 0 ) {
	fprintf( stderr, "merge: bad type [%s]\n", typestr ) ;
	exit( 1 ) ;
    }
    bytes = typebytes( type ) ;

    if ( range( rangestr, &a, &b, samplerate ) == 0 ) {
	fprintf(stderr,"merge: bad range [%s]\n", rangestr ) ;
	exit( 1 ) ;
    }


    fp     = (FILE **)malloc( i * sizeof( FILE *) ) ;
    vec    = (float *)malloc( i * sizeof( float ) ) ;
    origin = (int   *)malloc( i * sizeof( int   ) ) ;


    if ( isoff( wtstr ) ) weight = (float *)0 ;
    else {
	list   = (char **)malloc( i * sizeof( char *) ) ;
	weight = (float *)malloc( i * sizeof( float ) ) ;
	if ( ( n = tokens( wtstr, list, i, ',' ) ) != i ) {
	    fprintf( stderr,"merge: incorrect number of weights\n" ) ;
	    exit( 1 ) ;
	}
	for ( j = 0 ; j < n ; j++ )
	    weight[j] = atof( list[j] ) ;
    }

    if ( isoff( phstr ) )
	for ( j = 0 ; j < i ; j++ )
	    origin[j] = a ;
    else {
	list   = (char **)malloc( i * sizeof( char *) ) ;
	if ( ( n = tokens( phstr, list, i, ',' ) ) != i ) {
	    fprintf( stderr,"merge: incorrect number of phases\n" ) ;
	    exit( 1 ) ;
	}
	for ( j = 0 ; j < n ; j++ )
	    origin[j] = a + to_p( list[j], samplerate ) ;
    }



    for ( n = 0 ; i > 0 ; i--, n++ )  {
	if ( ( fp[n] = fropen( argv[argc-i] ) ) == (FILE *)0 ) {
	    fprintf( stderr,"merge: can't open %s\n", argv[argc-i] ) ;
	    exit( 1 ) ;
	}
    }


    for ( i = 0 ; i < n ; i++ )
	if ( origin[i] > 0 )
	    if ( seekstart( origin[i], bytes, fp[i] ) < origin[i] ) {
		fprintf( stderr, "insufficient data in file%d\n", i+1 ) ;
		exit( 1 ) ;
	    }


    for ( i = a ; ( i <= b || b == (-1) ) && readvec( vec, n, fp, type, origin ) ; i++ ) {

	if ( weight != (float *)0 )
	    do_weights( vec, weight, n ) ;

	j = do_operation( vec, n, op ) ;
	if ( ( f = writevec( vec, j, type, scale ) ) < newscale )
	    newscale = f ;
    }

    if ( newscale < 1. )
	fprintf( stderr, "Warning: 16-bit overflow during merge. Try scale<%.4f\n", newscale ) ;

}


/*
Return the operator index (to the operator list) of the given operator string.
*/

int opindex( opstr )
char *opstr ;
{
    int  i ;

    if ( ( i = listindex( operator, opstr ) ) < 0 ) {
	if ( i == (-1) )
	    fprintf(stderr,"merge: unknown operator [%s]\n", opstr ) ;
	if ( i == (-2) )
	    fprintf(stderr,"merge: ambiguous operator [%s]\n", opstr ) ;
	exit( 1 ) ;
    }
    return i ;
}



/*
Assign the given n-vector of floats using one item of given type (an index
to the datatype list in options.h) read from each of the n streams given in
the fp array. Return 0 if eof on any stream, otherwise return 1.
If the stream origin < 0 read a value of 0, increment the origin, and return
1 (successful read).
*/

readvec( vec, n, fp, type, origin )
float  *vec  ;
int     n    ;
FILE  **fp   ;
int     type ;
int    *origin ;
{
    int  i ;

    for ( i = 0 ; i < n ; i++ ) {

	if ( origin[i] < 0 ) {
	    vec[i] = 0  ;
	    origin[i]++ ;
	}

	else if ( readitem( &vec[i], type, 1, fp[i] ) == 0 )
	    return 0 ;

    }
    return 1 ;
}


/*
Write n elements from the given vector on the stdout in the given type
(an index to the datatype list in options.h)
Multiply each element by the given scale factor (prior to type conversion).
If any element will over or underflow when scaled and cast into type
then return a more appropriate scale factor.
Return scale factor of 1 if no over or underflow.
*/

float writevec( vec, n, type, scale )
float *vec  ;
int    n    ;
int    type ;
float  scale;
{
    float newscale ;
    int   i ;


    for ( i = 0 ; i < n ; i++ ) {

	newscale = check_overflow( vec[i], scale, type ) ;

	vec[i] *= scale ;
	writeitem( &vec[i], type, 1, stdout ) ;
    }

    return ( newscale ) ;
}



/*
Perform the given operation (op is an index to the operations list) on the
given n-vector, storing the results in the vector.
Return the size of the result (the number of elements of vec it occupies).
*/

do_operation( vec, n, op )
float *vec  ;
int    n    ;
int    op   ;
{
    int  i ;

    switch ( op ) {

	case Cat      : return n ;
	case Add      : for ( i = 1 ; i < n ; i++ )
			    vec[0] += vec[i] ;
			return 1 ;
	case Subtract : for ( i = 1 ; i < n ; i++ )
			    vec[0] -= vec[i] ;
			return 1 ;
	case Abs      : for ( i = 1 ; i < n ; i++ )
			    vec[0] = fabs(vec[0]-vec[i]) ;
			return 1 ;
	case Multiply : for ( i = 1 ; i < n ; i++ )
			    vec[0] *= vec[i] ;
			return 1 ;
	case Divide   : for ( i = 1 ; i < n ; i++ )
			    vec[0] /= vec[i] ;
			return 1 ;
	case Max      : for ( i = 1 ; i < n ; i++ )
			    if ( vec[i] > vec[0] ) vec[0] = vec[i] ;
			return 1 ;
	case Min      : for ( i = 1 ; i < n ; i++ )
			    if ( vec[i] < vec[0] ) vec[0] = vec[i] ;
			return 1 ;
	case Mean     : for ( i = 1 ; i < n ; i++ )
			    vec[0] += vec[i] ;
			vec[0] /= n ;
			return 1 ;
	case Norm:      vec[0] = sq(vec[0]) ;
			for ( i = 1 ; i < n ; i++ )
			    vec[0] += sq(vec[i]) ;
			vec[0] = sqrt(vec[0]) ;
			return 1 ;
    }
}


do_weights( vec, weight, n )
float *vec    ;
float *weight ;
int    n      ;
{
    int  i ;

    for ( i = 0 ; i < n ; i++ )
	vec[i] *= weight[i] ;
}


morehelp()
{
    fprintf(stderr,"\noperators:                       \n");
    fprintf(stderr,"            cat         concatenate  p1,p2,p3,... \n");
    fprintf(stderr,"            add         p1+p2+p3+...      \n");
    fprintf(stderr,"            subtract    p1-p2-p3-...      \n");
    fprintf(stderr,"            abs         ||p1-p2|-p3|-...    \n");
    fprintf(stderr,"            multiply    p1*p2*p3*...      \n");
    fprintf(stderr,"            divide      p1/p2/p3/...      \n");
    fprintf(stderr,"            max         max(p1,p2,p3,...) \n");
    fprintf(stderr,"            min         min(p1,p2,p3,...) \n");
    fprintf(stderr,"            mean        mean(p1,p2,p3,...) \n");
    fprintf(stderr,"            norm        sqrt( p1*p1+p2*p2+p3*p3+... )  \n");
    exit( 1 ) ;
}