tomwalters@0
|
1 /*
|
tomwalters@0
|
2 merge.c Merge N streams onto stdout using given operator.
|
tomwalters@0
|
3 -------
|
tomwalters@0
|
4
|
tomwalters@0
|
5 Usage: merge [options] file1 file2 ... fileN
|
tomwalters@0
|
6
|
tomwalters@0
|
7 For each vector of items (p1,p2,...,pN) read from file1, file2,...,fileN
|
tomwalters@0
|
8 respectively write the result of a merging operation on the stdout.
|
tomwalters@0
|
9
|
tomwalters@0
|
10 An input item is a number depending upon the type option.
|
tomwalters@0
|
11 The output item has the same type as that selected for input.
|
tomwalters@0
|
12
|
tomwalters@0
|
13 Internal processing is in floating point. A scale factor is included to
|
tomwalters@0
|
14 avoid overflow when casting output data.
|
tomwalters@0
|
15 If 16-bit over or under-flow occurs a warning is printed on the stderr.
|
tomwalters@0
|
16
|
tomwalters@0
|
17 The `range' option sets the start and duration of the process.
|
tomwalters@0
|
18 Its arguments are of the form: range=a-b (where start=a and duration=b-a+1)
|
tomwalters@0
|
19 or: range=a (where start=a and duration=1 )
|
tomwalters@0
|
20 The arguments can be in time units (ms, s) or samples (no units), and both
|
tomwalters@0
|
21 "min" (start of file) and "max" (end of file) are recognised.
|
tomwalters@0
|
22 Samples in a file are numbered 0,1,2,...,max.
|
tomwalters@0
|
23
|
tomwalters@0
|
24 All files are interpreted as functions sampled with origin at the point in
|
tomwalters@0
|
25 the file given by the first argument of the `range' option.
|
tomwalters@0
|
26 The functions are assumed to be zero for all samples beyond those specified
|
tomwalters@0
|
27 in the files.
|
tomwalters@0
|
28
|
tomwalters@0
|
29 The `phase' option shifts the origin to introduce a phase difference between
|
tomwalters@0
|
30 files. The `phase' option takes as argument a comma-separated list of
|
tomwalters@0
|
31 arguments which must be the same length as the number of file arguments.
|
tomwalters@0
|
32 Each argument can be in time units (ms, s) or samples (no units).
|
tomwalters@0
|
33 A +ve phase advances the origin into the file.
|
tomwalters@0
|
34 A -ve phase retards the origin. If a negative phase in combination with
|
tomwalters@0
|
35 the start of the file as set by the `range' option results in a new origin
|
tomwalters@0
|
36 outside the specified file, then the difference is padded with zeroes.
|
tomwalters@0
|
37
|
tomwalters@0
|
38 The weights option takes as argument a comma-separated list of real numbers
|
tomwalters@0
|
39 which must be the same length as the number of file arguments.
|
tomwalters@0
|
40 These number are applied as weights to each item prior to the merging
|
tomwalters@0
|
41 operation.
|
tomwalters@0
|
42
|
tomwalters@0
|
43 */
|
tomwalters@0
|
44
|
tomwalters@0
|
45 #include <stdio.h>
|
tomwalters@0
|
46 #include <math.h>
|
tomwalters@0
|
47 #include "options.h"
|
tomwalters@0
|
48 #include "strmatch.h"
|
tomwalters@0
|
49 #include "units.h"
|
tomwalters@0
|
50
|
tomwalters@0
|
51 char applic[] = "Merge N streams onto stdout using given operator." ;
|
tomwalters@0
|
52 char usage[] = "merge [options] file1 file2 ... fileN" ;
|
tomwalters@0
|
53
|
tomwalters@0
|
54 static char *helpstr, *debugstr, *sampstr, *opstr ;
|
tomwalters@0
|
55 static char *phstr, *rangestr, *scalestr, *typestr ;
|
tomwalters@0
|
56 static char *wtstr ;
|
tomwalters@0
|
57
|
tomwalters@0
|
58 static Options option[] = {
|
tomwalters@0
|
59 { "help" , "off" , &helpstr , "help" , DEBUG },
|
tomwalters@0
|
60 { "debug" , "off" , &debugstr , "debugging switch" , DEBUG },
|
tomwalters@0
|
61 { "samplerate", "20kHz" , &sampstr , "samplerate " , VAL },
|
tomwalters@0
|
62 { "operator" , "mean" , &opstr , "operator for merging" , VAL },
|
tomwalters@0
|
63 { "phase" , "off" , &phstr , "phase shift (phi1,...,phiN)" , VAL },
|
tomwalters@0
|
64 { "weights" , "off" , &wtstr , "weight coefficients (w1,...,wN)" , VAL },
|
tomwalters@0
|
65 { "range" , "0-max" , &rangestr , "inclusive time range for all files" , VAL },
|
tomwalters@0
|
66 { "scale" , "1.0" , &scalestr , "scale factor for output" , VAL },
|
tomwalters@0
|
67 { "type" , "short" , &typestr , "datatype" , VAL },
|
tomwalters@0
|
68 ( char * ) 0 } ;
|
tomwalters@0
|
69
|
tomwalters@0
|
70 char *operator[] = {
|
tomwalters@0
|
71 "cat" ,
|
tomwalters@0
|
72 "add" ,
|
tomwalters@0
|
73 "subtract" ,
|
tomwalters@0
|
74 "multiply" ,
|
tomwalters@0
|
75 "divide" ,
|
tomwalters@0
|
76 "abs" ,
|
tomwalters@0
|
77 "max" ,
|
tomwalters@0
|
78 "min" ,
|
tomwalters@0
|
79 "mean" ,
|
tomwalters@0
|
80 "norm" ,
|
tomwalters@0
|
81 ( char * ) 0 } ;
|
tomwalters@0
|
82
|
tomwalters@0
|
83
|
tomwalters@0
|
84 #define Cat 0 /* Functions (index of operator list) */
|
tomwalters@0
|
85 #define Add 1
|
tomwalters@0
|
86 #define Subtract 2
|
tomwalters@0
|
87 #define Multiply 3
|
tomwalters@0
|
88 #define Divide 4
|
tomwalters@0
|
89 #define Abs 5
|
tomwalters@0
|
90 #define Max 6
|
tomwalters@0
|
91 #define Min 7
|
tomwalters@0
|
92 #define Mean 8
|
tomwalters@0
|
93 #define Norm 9
|
tomwalters@0
|
94
|
tomwalters@0
|
95 #define sq(p) ((p)*(p))
|
tomwalters@0
|
96
|
tomwalters@0
|
97 int samplerate ;
|
tomwalters@0
|
98 int bytes ;
|
tomwalters@0
|
99 int type ; /* datatype index */
|
tomwalters@0
|
100 int op ; /* operator index */
|
tomwalters@0
|
101 float scale ;
|
tomwalters@0
|
102
|
tomwalters@0
|
103
|
tomwalters@0
|
104 main(argc, argv)
|
tomwalters@0
|
105 int argc;
|
tomwalters@0
|
106 char **argv;
|
tomwalters@0
|
107 {
|
tomwalters@0
|
108 FILE **fp ;
|
tomwalters@0
|
109 float *vec, *weight ;
|
tomwalters@0
|
110 int *origin ;
|
tomwalters@0
|
111 char **list ;
|
tomwalters@0
|
112 int i, j, n, morehelp() ;
|
tomwalters@0
|
113 int a, b ;
|
tomwalters@0
|
114 float writevec(), f, newscale = 1. ;
|
tomwalters@0
|
115
|
tomwalters@0
|
116 if ( ( i = getopts( option, argc, argv ) ) == 0 || !isoff( helpstr ) )
|
tomwalters@0
|
117 helpopts2( helpstr, argv[0], applic, usage, option, morehelp ) ;
|
tomwalters@0
|
118
|
tomwalters@0
|
119 samplerate = to_Hz( sampstr, 0 ) ;
|
tomwalters@0
|
120 scale = atof( scalestr ) ;
|
tomwalters@0
|
121 op = opindex( opstr ) ;
|
tomwalters@0
|
122
|
tomwalters@0
|
123 if ( ( type = typeindex( typestr ) ) < 0 ) {
|
tomwalters@0
|
124 fprintf( stderr, "merge: bad type [%s]\n", typestr ) ;
|
tomwalters@0
|
125 exit( 1 ) ;
|
tomwalters@0
|
126 }
|
tomwalters@0
|
127 bytes = typebytes( type ) ;
|
tomwalters@0
|
128
|
tomwalters@0
|
129 if ( range( rangestr, &a, &b, samplerate ) == 0 ) {
|
tomwalters@0
|
130 fprintf(stderr,"merge: bad range [%s]\n", rangestr ) ;
|
tomwalters@0
|
131 exit( 1 ) ;
|
tomwalters@0
|
132 }
|
tomwalters@0
|
133
|
tomwalters@0
|
134
|
tomwalters@0
|
135 fp = (FILE **)malloc( i * sizeof( FILE *) ) ;
|
tomwalters@0
|
136 vec = (float *)malloc( i * sizeof( float ) ) ;
|
tomwalters@0
|
137 origin = (int *)malloc( i * sizeof( int ) ) ;
|
tomwalters@0
|
138
|
tomwalters@0
|
139
|
tomwalters@0
|
140 if ( isoff( wtstr ) ) weight = (float *)0 ;
|
tomwalters@0
|
141 else {
|
tomwalters@0
|
142 list = (char **)malloc( i * sizeof( char *) ) ;
|
tomwalters@0
|
143 weight = (float *)malloc( i * sizeof( float ) ) ;
|
tomwalters@0
|
144 if ( ( n = tokens( wtstr, list, i, ',' ) ) != i ) {
|
tomwalters@0
|
145 fprintf( stderr,"merge: incorrect number of weights\n" ) ;
|
tomwalters@0
|
146 exit( 1 ) ;
|
tomwalters@0
|
147 }
|
tomwalters@0
|
148 for ( j = 0 ; j < n ; j++ )
|
tomwalters@0
|
149 weight[j] = atof( list[j] ) ;
|
tomwalters@0
|
150 }
|
tomwalters@0
|
151
|
tomwalters@0
|
152 if ( isoff( phstr ) )
|
tomwalters@0
|
153 for ( j = 0 ; j < i ; j++ )
|
tomwalters@0
|
154 origin[j] = a ;
|
tomwalters@0
|
155 else {
|
tomwalters@0
|
156 list = (char **)malloc( i * sizeof( char *) ) ;
|
tomwalters@0
|
157 if ( ( n = tokens( phstr, list, i, ',' ) ) != i ) {
|
tomwalters@0
|
158 fprintf( stderr,"merge: incorrect number of phases\n" ) ;
|
tomwalters@0
|
159 exit( 1 ) ;
|
tomwalters@0
|
160 }
|
tomwalters@0
|
161 for ( j = 0 ; j < n ; j++ )
|
tomwalters@0
|
162 origin[j] = a + to_p( list[j], samplerate ) ;
|
tomwalters@0
|
163 }
|
tomwalters@0
|
164
|
tomwalters@0
|
165
|
tomwalters@0
|
166
|
tomwalters@0
|
167 for ( n = 0 ; i > 0 ; i--, n++ ) {
|
tomwalters@0
|
168 if ( ( fp[n] = fropen( argv[argc-i] ) ) == (FILE *)0 ) {
|
tomwalters@0
|
169 fprintf( stderr,"merge: can't open %s\n", argv[argc-i] ) ;
|
tomwalters@0
|
170 exit( 1 ) ;
|
tomwalters@0
|
171 }
|
tomwalters@0
|
172 }
|
tomwalters@0
|
173
|
tomwalters@0
|
174
|
tomwalters@0
|
175 for ( i = 0 ; i < n ; i++ )
|
tomwalters@0
|
176 if ( origin[i] > 0 )
|
tomwalters@0
|
177 if ( seekstart( origin[i], bytes, fp[i] ) < origin[i] ) {
|
tomwalters@0
|
178 fprintf( stderr, "insufficient data in file%d\n", i+1 ) ;
|
tomwalters@0
|
179 exit( 1 ) ;
|
tomwalters@0
|
180 }
|
tomwalters@0
|
181
|
tomwalters@0
|
182
|
tomwalters@0
|
183 for ( i = a ; ( i <= b || b == (-1) ) && readvec( vec, n, fp, type, origin ) ; i++ ) {
|
tomwalters@0
|
184
|
tomwalters@0
|
185 if ( weight != (float *)0 )
|
tomwalters@0
|
186 do_weights( vec, weight, n ) ;
|
tomwalters@0
|
187
|
tomwalters@0
|
188 j = do_operation( vec, n, op ) ;
|
tomwalters@0
|
189 if ( ( f = writevec( vec, j, type, scale ) ) < newscale )
|
tomwalters@0
|
190 newscale = f ;
|
tomwalters@0
|
191 }
|
tomwalters@0
|
192
|
tomwalters@0
|
193 if ( newscale < 1. )
|
tomwalters@0
|
194 fprintf( stderr, "Warning: 16-bit overflow during merge. Try scale<%.4f\n", newscale ) ;
|
tomwalters@0
|
195
|
tomwalters@0
|
196 }
|
tomwalters@0
|
197
|
tomwalters@0
|
198
|
tomwalters@0
|
199 /*
|
tomwalters@0
|
200 Return the operator index (to the operator list) of the given operator string.
|
tomwalters@0
|
201 */
|
tomwalters@0
|
202
|
tomwalters@0
|
203 int opindex( opstr )
|
tomwalters@0
|
204 char *opstr ;
|
tomwalters@0
|
205 {
|
tomwalters@0
|
206 int i ;
|
tomwalters@0
|
207
|
tomwalters@0
|
208 if ( ( i = listindex( operator, opstr ) ) < 0 ) {
|
tomwalters@0
|
209 if ( i == (-1) )
|
tomwalters@0
|
210 fprintf(stderr,"merge: unknown operator [%s]\n", opstr ) ;
|
tomwalters@0
|
211 if ( i == (-2) )
|
tomwalters@0
|
212 fprintf(stderr,"merge: ambiguous operator [%s]\n", opstr ) ;
|
tomwalters@0
|
213 exit( 1 ) ;
|
tomwalters@0
|
214 }
|
tomwalters@0
|
215 return i ;
|
tomwalters@0
|
216 }
|
tomwalters@0
|
217
|
tomwalters@0
|
218
|
tomwalters@0
|
219
|
tomwalters@0
|
220 /*
|
tomwalters@0
|
221 Assign the given n-vector of floats using one item of given type (an index
|
tomwalters@0
|
222 to the datatype list in options.h) read from each of the n streams given in
|
tomwalters@0
|
223 the fp array. Return 0 if eof on any stream, otherwise return 1.
|
tomwalters@0
|
224 If the stream origin < 0 read a value of 0, increment the origin, and return
|
tomwalters@0
|
225 1 (successful read).
|
tomwalters@0
|
226 */
|
tomwalters@0
|
227
|
tomwalters@0
|
228 readvec( vec, n, fp, type, origin )
|
tomwalters@0
|
229 float *vec ;
|
tomwalters@0
|
230 int n ;
|
tomwalters@0
|
231 FILE **fp ;
|
tomwalters@0
|
232 int type ;
|
tomwalters@0
|
233 int *origin ;
|
tomwalters@0
|
234 {
|
tomwalters@0
|
235 int i ;
|
tomwalters@0
|
236
|
tomwalters@0
|
237 for ( i = 0 ; i < n ; i++ ) {
|
tomwalters@0
|
238
|
tomwalters@0
|
239 if ( origin[i] < 0 ) {
|
tomwalters@0
|
240 vec[i] = 0 ;
|
tomwalters@0
|
241 origin[i]++ ;
|
tomwalters@0
|
242 }
|
tomwalters@0
|
243
|
tomwalters@0
|
244 else if ( readitem( &vec[i], type, 1, fp[i] ) == 0 )
|
tomwalters@0
|
245 return 0 ;
|
tomwalters@0
|
246
|
tomwalters@0
|
247 }
|
tomwalters@0
|
248 return 1 ;
|
tomwalters@0
|
249 }
|
tomwalters@0
|
250
|
tomwalters@0
|
251
|
tomwalters@0
|
252 /*
|
tomwalters@0
|
253 Write n elements from the given vector on the stdout in the given type
|
tomwalters@0
|
254 (an index to the datatype list in options.h)
|
tomwalters@0
|
255 Multiply each element by the given scale factor (prior to type conversion).
|
tomwalters@0
|
256 If any element will over or underflow when scaled and cast into type
|
tomwalters@0
|
257 then return a more appropriate scale factor.
|
tomwalters@0
|
258 Return scale factor of 1 if no over or underflow.
|
tomwalters@0
|
259 */
|
tomwalters@0
|
260
|
tomwalters@0
|
261 float writevec( vec, n, type, scale )
|
tomwalters@0
|
262 float *vec ;
|
tomwalters@0
|
263 int n ;
|
tomwalters@0
|
264 int type ;
|
tomwalters@0
|
265 float scale;
|
tomwalters@0
|
266 {
|
tomwalters@0
|
267 float newscale ;
|
tomwalters@0
|
268 int i ;
|
tomwalters@0
|
269
|
tomwalters@0
|
270
|
tomwalters@0
|
271 for ( i = 0 ; i < n ; i++ ) {
|
tomwalters@0
|
272
|
tomwalters@0
|
273 newscale = check_overflow( vec[i], scale, type ) ;
|
tomwalters@0
|
274
|
tomwalters@0
|
275 vec[i] *= scale ;
|
tomwalters@0
|
276 writeitem( &vec[i], type, 1, stdout ) ;
|
tomwalters@0
|
277 }
|
tomwalters@0
|
278
|
tomwalters@0
|
279 return ( newscale ) ;
|
tomwalters@0
|
280 }
|
tomwalters@0
|
281
|
tomwalters@0
|
282
|
tomwalters@0
|
283
|
tomwalters@0
|
284 /*
|
tomwalters@0
|
285 Perform the given operation (op is an index to the operations list) on the
|
tomwalters@0
|
286 given n-vector, storing the results in the vector.
|
tomwalters@0
|
287 Return the size of the result (the number of elements of vec it occupies).
|
tomwalters@0
|
288 */
|
tomwalters@0
|
289
|
tomwalters@0
|
290 do_operation( vec, n, op )
|
tomwalters@0
|
291 float *vec ;
|
tomwalters@0
|
292 int n ;
|
tomwalters@0
|
293 int op ;
|
tomwalters@0
|
294 {
|
tomwalters@0
|
295 int i ;
|
tomwalters@0
|
296
|
tomwalters@0
|
297 switch ( op ) {
|
tomwalters@0
|
298
|
tomwalters@0
|
299 case Cat : return n ;
|
tomwalters@0
|
300 case Add : for ( i = 1 ; i < n ; i++ )
|
tomwalters@0
|
301 vec[0] += vec[i] ;
|
tomwalters@0
|
302 return 1 ;
|
tomwalters@0
|
303 case Subtract : for ( i = 1 ; i < n ; i++ )
|
tomwalters@0
|
304 vec[0] -= vec[i] ;
|
tomwalters@0
|
305 return 1 ;
|
tomwalters@0
|
306 case Abs : for ( i = 1 ; i < n ; i++ )
|
tomwalters@0
|
307 vec[0] = fabs(vec[0]-vec[i]) ;
|
tomwalters@0
|
308 return 1 ;
|
tomwalters@0
|
309 case Multiply : for ( i = 1 ; i < n ; i++ )
|
tomwalters@0
|
310 vec[0] *= vec[i] ;
|
tomwalters@0
|
311 return 1 ;
|
tomwalters@0
|
312 case Divide : for ( i = 1 ; i < n ; i++ )
|
tomwalters@0
|
313 vec[0] /= vec[i] ;
|
tomwalters@0
|
314 return 1 ;
|
tomwalters@0
|
315 case Max : for ( i = 1 ; i < n ; i++ )
|
tomwalters@0
|
316 if ( vec[i] > vec[0] ) vec[0] = vec[i] ;
|
tomwalters@0
|
317 return 1 ;
|
tomwalters@0
|
318 case Min : for ( i = 1 ; i < n ; i++ )
|
tomwalters@0
|
319 if ( vec[i] < vec[0] ) vec[0] = vec[i] ;
|
tomwalters@0
|
320 return 1 ;
|
tomwalters@0
|
321 case Mean : for ( i = 1 ; i < n ; i++ )
|
tomwalters@0
|
322 vec[0] += vec[i] ;
|
tomwalters@0
|
323 vec[0] /= n ;
|
tomwalters@0
|
324 return 1 ;
|
tomwalters@0
|
325 case Norm: vec[0] = sq(vec[0]) ;
|
tomwalters@0
|
326 for ( i = 1 ; i < n ; i++ )
|
tomwalters@0
|
327 vec[0] += sq(vec[i]) ;
|
tomwalters@0
|
328 vec[0] = sqrt(vec[0]) ;
|
tomwalters@0
|
329 return 1 ;
|
tomwalters@0
|
330 }
|
tomwalters@0
|
331 }
|
tomwalters@0
|
332
|
tomwalters@0
|
333
|
tomwalters@0
|
334 do_weights( vec, weight, n )
|
tomwalters@0
|
335 float *vec ;
|
tomwalters@0
|
336 float *weight ;
|
tomwalters@0
|
337 int n ;
|
tomwalters@0
|
338 {
|
tomwalters@0
|
339 int i ;
|
tomwalters@0
|
340
|
tomwalters@0
|
341 for ( i = 0 ; i < n ; i++ )
|
tomwalters@0
|
342 vec[i] *= weight[i] ;
|
tomwalters@0
|
343 }
|
tomwalters@0
|
344
|
tomwalters@0
|
345
|
tomwalters@0
|
346 morehelp()
|
tomwalters@0
|
347 {
|
tomwalters@0
|
348 fprintf(stderr,"\noperators: \n");
|
tomwalters@0
|
349 fprintf(stderr," cat concatenate p1,p2,p3,... \n");
|
tomwalters@0
|
350 fprintf(stderr," add p1+p2+p3+... \n");
|
tomwalters@0
|
351 fprintf(stderr," subtract p1-p2-p3-... \n");
|
tomwalters@0
|
352 fprintf(stderr," abs ||p1-p2|-p3|-... \n");
|
tomwalters@0
|
353 fprintf(stderr," multiply p1*p2*p3*... \n");
|
tomwalters@0
|
354 fprintf(stderr," divide p1/p2/p3/... \n");
|
tomwalters@0
|
355 fprintf(stderr," max max(p1,p2,p3,...) \n");
|
tomwalters@0
|
356 fprintf(stderr," min min(p1,p2,p3,...) \n");
|
tomwalters@0
|
357 fprintf(stderr," mean mean(p1,p2,p3,...) \n");
|
tomwalters@0
|
358 fprintf(stderr," norm sqrt( p1*p1+p2*p2+p3*p3+... ) \n");
|
tomwalters@0
|
359 exit( 1 ) ;
|
tomwalters@0
|
360 }
|
tomwalters@0
|
361
|
tomwalters@0
|
362
|