3 * gtk2 spectrum analyzer
5 * Copyright (C) 2004 Monty
7 * This analyzer is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
12 * The analyzer is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Postfish; see the file COPYING. If not, write to the
19 * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
26 static int blockslice
[MAX_FILES
]= {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
28 static float **blockbuffer
=0;
29 static int blockbufferfill
[MAX_FILES
]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
31 static float *freqbuffer
=0;
32 static fftwf_plan plan
;
34 static unsigned char readbuffer
[MAX_FILES
][readbuffersize
];
35 static int readbufferfill
[MAX_FILES
]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
36 static int readbufferptr
[MAX_FILES
]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
38 static FILE *f
[MAX_FILES
]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
39 static off_t offset
[MAX_FILES
]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
40 static off_t length
[MAX_FILES
]= {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
41 static off_t bytesleft
[MAX_FILES
]= {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
42 int seekable
[MAX_FILES
]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
43 int global_seekable
=0;
45 pthread_mutex_t feedback_mutex
=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
;
46 int feedback_increment
=0;
48 float *feedback_count
;
50 float *plot_floor
=NULL
;
51 float **work_floor
=NULL
;
56 float **feedback_instant
;
58 /* Gentlemen, power up the Variance hammer */
74 sig_atomic_t acc_clear
=0;
75 sig_atomic_t acc_rewind
=0;
76 sig_atomic_t acc_loop
=0;
78 sig_atomic_t process_active
=0;
79 sig_atomic_t process_exit
=0;
81 static int host_is_big_endian() {
82 int32_t pattern
= 0xfeedface; /* deadbeef */
83 unsigned char *bytewise
= (unsigned char *)&pattern
;
84 if (bytewise
[0] == 0xfe) return 1;
88 /* Macros to read header data */
89 #define READ_U32_LE(buf) \
90 (((buf)[3]<<24)|((buf)[2]<<16)|((buf)[1]<<8)|((buf)[0]&0xff))
92 #define READ_U16_LE(buf) \
93 (((buf)[1]<<8)|((buf)[0]&0xff))
95 #define READ_U32_BE(buf) \
96 (((buf)[0]<<24)|((buf)[1]<<16)|((buf)[2]<<8)|((buf)[3]&0xff))
98 #define READ_U16_BE(buf) \
99 (((buf)[0]<<8)|((buf)[1]&0xff))
101 double read_IEEE80(unsigned char *buf
){
103 int e
=((buf
[0]&0x7f)<<8)|(buf
[1]&0xff);
104 double f
=((unsigned long)(buf
[2]&0xff)<<24)|
111 return HUGE_VAL
; /* Really NaN, but this won't happen in reality */
121 f
+= ((buf
[6]&0xff)<<24)|
126 return ldexp(f
, e
-16446);
129 static int find_chunk(FILE *in
, char *type
, unsigned int *len
, int endian
){
131 unsigned char buf
[8];
134 if(fread(buf
,1,8,in
) <8)return 0;
137 *len
= READ_U32_BE(buf
+4);
139 *len
= READ_U32_LE(buf
+4);
141 if(memcmp(buf
,type
,4)){
143 if((*len
) & 0x1)(*len
)++;
146 if(fgetc(in
)==EOF
)return 0;
152 int input_load(void){
156 /* look at stdin... is it a file, pipe, tty...? */
157 if(isatty(STDIN_FILENO
)){
159 "Spectrum requires either an input file on the command line\n"
160 "or stream data piped|redirected to stdin. spectrum -h will\n"
161 "give more details.\n");
164 stdinp
=1; /* file coming in via stdin */
165 inputname
[0]=strdup("stdin");
169 for(fi
=0;fi
<inputs
;fi
++){
172 int newfd
=dup(STDIN_FILENO
);
173 f
[fi
]=fdopen(newfd
,"rb");
175 f
[fi
]=fopen(inputname
[fi
],"rb");
179 /* Crappy! Use a lib to do this for pete's sake! */
184 /* parse header (well, sort of) and get file size */
185 seekable
[fi
]=(fseek(f
[fi
],0,SEEK_CUR
)?0:1);
190 fseek(f
[fi
],0,SEEK_END
);
191 filelength
=ftello(f
[fi
]);
192 fseek(f
[fi
],0,SEEK_SET
);
196 fread(headerid
,1,12,f
[fi
]);
197 if(!strncmp(headerid
,"RIFF",4) && !strncmp(headerid
+8,"WAVE",4)){
198 unsigned int chunklen
;
200 if(find_chunk(f
[fi
],"fmt ",&chunklen
,0)){
205 unsigned char *buf
=alloca(chunklen
);
207 fread(buf
,1,chunklen
,f
[fi
]);
209 ltype
= READ_U16_LE(buf
);
210 lch
= READ_U16_LE(buf
+2);
211 lrate
= READ_U32_LE(buf
+4);
212 lbits
= READ_U16_LE(buf
+14);
215 fprintf(stderr
,"%s:\n\tWAVE file not PCM.\n",inputname
[fi
]);
219 if(bits
[fi
]==-1)bits
[fi
]=lbits
;
220 if(channels
[fi
]==-1)channels
[fi
]=lch
;
223 if(bits
[fi
]>8)signedp
[fi
]=1;
225 if(bigendian
[fi
]==-1)bigendian
[fi
]=0;
227 if(lrate
<4000 || lrate
>192000){
228 fprintf(stderr
,"%s:\n\tSampling rate out of bounds\n",inputname
[fi
]);
234 if(find_chunk(f
[fi
],"data",&chunklen
,0)){
235 off_t pos
=ftello(f
[fi
]);
236 int bytes
=(bits
[fi
]+7)/8;
240 (channels
[fi
]*bytes
)*
241 (channels
[fi
]*bytes
)+pos
;
244 chunklen
==0x7fffffffUL
||
245 chunklen
==0xffffffffUL
){
248 fprintf(stderr
,"%s: Incomplete header; assuming stream.\n",inputname
[fi
]);
250 length
[fi
]=(filelength
-pos
)/(channels
[fi
]*bytes
);
251 fprintf(stderr
,"%s: Incomplete header; using actual file size.\n",inputname
[fi
]);
253 }else if(filelength
==-1 || chunklen
+pos
<=filelength
){
254 length
[fi
]=(chunklen
/(channels
[fi
]*bytes
));
255 fprintf(stderr
,"%s: Using declared file size (%ld).\n",
256 inputname
[fi
],(long)length
[fi
]*channels
[fi
]*bytes
);
260 length
[fi
]=(filelength
-pos
)/(channels
[fi
]*bytes
);
261 fprintf(stderr
,"%s: File truncated; Using actual file size.\n",inputname
[fi
]);
263 offset
[fi
]=ftello(f
[fi
]);
265 fprintf(stderr
,"%s: WAVE file has no \"data\" chunk following \"fmt \".\n",inputname
[fi
]);
269 fprintf(stderr
,"%s: WAVE file has no \"fmt \" chunk.\n",inputname
[fi
]);
273 }else if(!strncmp(headerid
,"FORM",4) && !strncmp(headerid
+8,"AIF",3)){
276 if(headerid
[11]=='C')aifc
=1;
277 unsigned char *buffer
;
286 if(!find_chunk(f
[fi
], "COMM", &len
,1)){
287 fprintf(stderr
,"%s: AIFF file has no \"COMM\" chunk.\n",inputname
[fi
]);
291 if(len
< 18 || (aifc
&& len
<22)) {
292 fprintf(stderr
,"%s: AIFF COMM chunk is truncated.\n",inputname
[fi
]);
296 buffer
= alloca(len
);
298 if(fread(buffer
,1,len
,f
[fi
]) < len
){
299 fprintf(stderr
, "%s: Unexpected EOF in reading AIFF header\n",inputname
[fi
]);
303 lch
= READ_U16_BE(buffer
);
304 lbits
= READ_U16_BE(buffer
+6);
305 lrate
= (int)read_IEEE80(buffer
+8);
307 if(bits
[fi
]==-1)bits
[fi
]=lbits
;
308 bytes
=(bits
[fi
]+7)/8;
309 if(signedp
[fi
]==-1)signedp
[fi
]=1;
311 if(lrate
<4000 || lrate
>192000){
312 fprintf(stderr
,"%s:\n\tSampling rate out of bounds\n",inputname
[fi
]);
317 if(channels
[fi
]==-1)channels
[fi
]=lch
;
319 if(bigendian
[fi
]==-1){
321 if(!memcmp(buffer
+18, "NONE", 4)) {
323 }else if(!memcmp(buffer
+18, "sowt", 4)) {
326 fprintf(stderr
, "%s: Spectrum supports only linear PCM AIFF-C files.\n",inputname
[fi
]);
332 if(!find_chunk(f
[fi
], "SSND", &len
, 1)){
333 fprintf(stderr
,"%s: AIFF file has no \"SSND\" chunk.\n",inputname
[fi
]);
337 if(fread(buf2
,1,8,f
[fi
]) < 8){
338 fprintf(stderr
,"%s: Unexpected EOF reading AIFF header\n",inputname
[fi
]);
343 int loffset
= READ_U32_BE(buf2
);
344 int lblocksize
= READ_U32_BE(buf2
+4);
346 /* swallow some data */
347 for(i
=0;i
<loffset
;i
++)
348 if(fgetc(f
[fi
])==EOF
)break;
350 if( lblocksize
== 0 && (bits
[fi
] == 24 || bits
[fi
] == 16 || bits
[fi
] == 8)){
352 off_t pos
=ftello(f
[fi
]);
357 (channels
[fi
]*bytes
)*
358 (channels
[fi
]*bytes
)+pos
;
365 fprintf(stderr
,"%s: Incomplete header; assuming stream.\n",inputname
[fi
]);
367 length
[fi
]=(filelength
-pos
)/(channels
[fi
]*bytes
);
368 fprintf(stderr
,"%s: Incomplete header; using actual file size.\n",inputname
[fi
]);
370 }else if(filelength
==-1 || (len
+pos
-loffset
-8)<=filelength
){
371 length
[fi
]=((len
-loffset
-8)/(channels
[fi
]*bytes
));
372 fprintf(stderr
,"%s: Using declared file size.\n",inputname
[fi
]);
375 length
[fi
]=(filelength
-pos
)/(channels
[fi
]*bytes
);
376 fprintf(stderr
,"%s: File truncated; Using actual file size.\n",inputname
[fi
]);
380 fprintf(stderr
, "%s: Spectrum supports only linear PCM AIFF-C files.\n",inputname
[fi
]);
385 /* must be raw input */
386 fprintf(stderr
,"Input has no header; assuming raw stream/file.\n");
388 if(channels
[fi
]==-1)channels
[fi
]=1;
389 if(rate
[fi
]==-1)rate
[fi
]=44100;
390 if(bits
[fi
]==-1)bits
[fi
]=16;
391 if(signedp
[fi
]==-1)signedp
[fi
]=1;
392 if(bigendian
[fi
]==-1)bigendian
[fi
]=host_is_big_endian();
396 if(seekable
[fi
])length
[fi
]=filelength
/(channels
[fi
]*((bits
[fi
]+7)/8));
398 memcpy(readbuffer
[fi
],headerid
,12);
399 readbufferfill
[fi
]=12;
403 /* select the full-block slice size: ~10fps */
404 blockslice
[fi
]=rate
[fi
]/10;
405 while(blockslice
[fi
]>blocksize
/2)blockslice
[fi
]/=2;
406 total_ch
+= channels
[fi
];
409 bytesleft
[fi
]=length
[fi
]*channels
[fi
]*((bits
[fi
]+7)/8);
412 fprintf(stderr
,"Unable to open %s: %s\n",inputname
[fi
],strerror(errno
));
417 blockbuffer
=malloc(total_ch
*sizeof(*blockbuffer
));
418 process_work
=calloc(blocksize
+2,sizeof(*process_work
));
419 feedback_count
=calloc(total_ch
,sizeof(*feedback_count
));
420 plot_data
=calloc(total_ch
,sizeof(*plot_data
));
422 feedback_acc
=malloc(total_ch
*sizeof(*feedback_acc
));
423 feedback_max
=malloc(total_ch
*sizeof(*feedback_max
));
424 feedback_instant
=malloc(total_ch
*sizeof(*feedback_instant
));
425 floor_y
=malloc(total_ch
*sizeof(*floor_y
));
426 floor_yy
=malloc(total_ch
*sizeof(*floor_yy
));
428 ph_acc
=malloc(total_ch
*sizeof(*ph_acc
));
429 ph_max
=malloc(total_ch
*sizeof(*ph_max
));
430 ph_instant
=malloc(total_ch
*sizeof(*ph_instant
));
432 freqbuffer
=fftwf_malloc((blocksize
+2)*sizeof(*freqbuffer
));
433 for(i
=0;i
<total_ch
;i
++){
434 blockbuffer
[i
]=calloc(blocksize
,sizeof(**blockbuffer
));
436 floor_y
[i
]=calloc(blocksize
/2+1,sizeof(**floor_y
));
437 floor_yy
[i
]=calloc(blocksize
/2+1,sizeof(**floor_yy
));
438 feedback_acc
[i
]=calloc(blocksize
/2+1,sizeof(**feedback_acc
));
439 feedback_max
[i
]=calloc(blocksize
/2+1,sizeof(**feedback_max
));
440 feedback_instant
[i
]=calloc(blocksize
/2+1,sizeof(**feedback_instant
));
442 ph_acc
[i
]=calloc(blocksize
+2,sizeof(**ph_acc
));
443 ph_max
[i
]=calloc(blocksize
+2,sizeof(**ph_max
));
444 ph_instant
[i
]=calloc(blocksize
+2,sizeof(**ph_instant
));
447 plan
=fftwf_plan_dft_r2c_1d(blocksize
,freqbuffer
,
448 (fftwf_complex
*)freqbuffer
,
451 /* construct proper window (sin^4 I'd think) */
452 window
= calloc(blocksize
,sizeof(*window
));
453 for(i
=0;i
<blocksize
;i
++)window
[i
]=sin(M_PIl
*i
/blocksize
);
454 for(i
=0;i
<blocksize
;i
++)window
[i
]*=window
[i
];
455 for(i
=0;i
<blocksize
;i
++)window
[i
]=sin(window
[i
]*M_PIl
*.5);
456 for(i
=0;i
<blocksize
;i
++)window
[i
]*=window
[i
]/(blocksize
/4)*.778;
462 /* Convert new data from readbuffer into the blockbuffers until the
463 blockbuffer is full */
464 static void LBEconvert(void){
465 float scale
=1./2147483648.;
468 for(fi
=0;fi
<inputs
;fi
++){
469 int bytes
=(bits
[fi
]+7)/8;
471 int32_t xor=(signedp
[fi
]?0:0x80000000UL
);
473 int readlimit
=(readbufferfill
[fi
]-readbufferptr
[fi
])/
474 channels
[fi
]/bytes
*channels
[fi
]*bytes
+readbufferptr
[fi
];
476 int bfill
= blockbufferfill
[fi
];
477 int rptr
= readbufferptr
[fi
];
478 unsigned char *rbuf
= readbuffer
[fi
];
485 while(bfill
<blocksize
&& rptr
<readlimit
){
486 for(j
=ch
;j
<channels
[fi
]+ch
;j
++)
487 blockbuffer
[j
][bfill
]=((rbuf
[rptr
++]<<24)^xor)*scale
;
495 while(bfill
<blocksize
&& rptr
<readlimit
){
496 for(j
=ch
;j
<channels
[fi
]+ch
;j
++){
497 blockbuffer
[j
][bfill
]=
498 (((rbuf
[rptr
+1]<<16)| (rbuf
[rptr
]<<24))^xor)*scale
;
504 while(bfill
<blocksize
&& rptr
<readlimit
){
505 for(j
=ch
;j
<channels
[fi
]+ch
;j
++){
506 blockbuffer
[j
][bfill
]=
507 (((rbuf
[rptr
]<<16)| (rbuf
[rptr
+1]<<24))^xor)*scale
;
518 while(bfill
<blocksize
&& rptr
<readlimit
){
519 for(j
=ch
;j
<channels
[fi
]+ch
;j
++){
520 blockbuffer
[j
][bfill
]=
521 (((rbuf
[rptr
+2]<<8)|(rbuf
[rptr
+1]<<16)|(rbuf
[rptr
]<<24))^xor)*scale
;
527 while(bfill
<blocksize
&& rptr
<readlimit
){
528 for(j
=ch
;j
<channels
[fi
]+ch
;j
++){
529 blockbuffer
[j
][bfill
]=
530 (((rbuf
[rptr
]<<8)|(rbuf
[rptr
+1]<<16)|(rbuf
[rptr
+2]<<24))^xor)*scale
;
540 while(bfill
<blocksize
&& rptr
<readlimit
){
541 for(j
=ch
;j
<channels
[fi
]+ch
;j
++){
542 blockbuffer
[j
][bfill
]=
543 (((rbuf
[rptr
+3])|(rbuf
[rptr
+2]<<8)|(rbuf
[rptr
+1]<<16)|(rbuf
[rptr
+3]<<24))^xor)*scale
;
549 while(bfill
<blocksize
&& rptr
<readlimit
){
550 for(j
=ch
;j
<channels
[fi
]+ch
;j
++){
551 blockbuffer
[j
][bfill
]=
552 (((rbuf
[rptr
])|(rbuf
[rptr
+1]<<8)|(rbuf
[rptr
+2]<<16)|(rbuf
[rptr
+3]<<24))^xor)*scale
;
562 blockbufferfill
[fi
]=bfill
;
563 readbufferptr
[fi
]=rptr
;
567 /* when we return, the blockbuffer is full or we're at EOF */
569 loop set: return EOF if all seekable streams have hit EOF
570 loop unset: return EOF if all streams have hit EOF
571 pad individual EOF streams out with zeroes until global EOF is hit */
573 static int input_read(void){
578 for(fi
=0;fi
<inputs
;fi
++){
580 /* shift according to slice */
581 if(blockbufferfill
[fi
]==blocksize
){
582 if(blockslice
[fi
]<blocksize
){
583 for(i
=0;i
<channels
[fi
];i
++)
584 memmove(blockbuffer
[i
+ch
],blockbuffer
[i
+ch
]+blockslice
[fi
],
585 (blocksize
-blockslice
[fi
])*sizeof(**blockbuffer
));
586 blockbufferfill
[fi
]-=blockslice
[fi
];
588 blockbufferfill
[fi
]=0;
596 /* if there's data left to be pulled out of a readbuffer, do that */
600 for(fi
=0;fi
<inputs
;fi
++){
601 if(blockbufferfill
[fi
]!=blocksize
){
603 /* shift the read buffer before fill if there's a fractional
605 if(readbufferptr
[fi
]!=readbufferfill
[fi
] && readbufferptr
[fi
]>0){
606 memmove(readbuffer
[fi
],readbuffer
[fi
]+readbufferptr
[fi
],
607 (readbufferfill
[fi
]-readbufferptr
[fi
])*sizeof(**readbuffer
));
608 readbufferfill
[fi
]-=readbufferptr
[fi
];
611 readbufferfill
[fi
]=0;
615 /* attempt to top off the readbuffer */
617 long actually_readbytes
=0,readbytes
=readbuffersize
-readbufferfill
[fi
];
620 actually_readbytes
=fread(readbuffer
[fi
]+readbufferfill
[fi
],1,readbytes
,f
[fi
]);
622 if(actually_readbytes
<0){
623 fprintf(stderr
,"Input read error from %s: %s\n",inputname
[fi
],strerror(errno
));
624 }else if (actually_readbytes
==0){
625 /* don't process any partially-filled blocks; the
626 stairstep at the end could pollute results badly */
628 memset(readbuffer
[fi
],0,readbuffersize
);
630 readbufferfill
[fi
]=0;
632 blockbufferfill
[fi
]=0;
635 bytesleft
[fi
]-=actually_readbytes
;
636 readbufferfill
[fi
]+=actually_readbytes
;
638 /* conditionally clear global EOF */
640 if(seekable
[fi
])eof
=0;
654 void rundata_clear(){
656 for(i
=0;i
<total_ch
;i
++){
658 memset(feedback_acc
[i
],0,(blocksize
/2+1)*sizeof(**feedback_acc
));
659 memset(feedback_max
[i
],0,(blocksize
/2+1)*sizeof(**feedback_max
));
660 memset(feedback_instant
[i
],0,(blocksize
/2+1)*sizeof(**feedback_instant
));
662 for(j
=0;j
<blocksize
+2;j
++){
671 extern int plot_noise
;
673 /* return 0 on EOF, 1 otherwise */
674 static int process(){
677 int noise
=plot_noise
;
679 /* for each file, FOR SCIENCE! */
680 for(fi
=0;fi
<inputs
;fi
++){
681 if(acc_rewind
&& seekable
[fi
]){
683 blockbufferfill
[fi
]=0;
685 readbufferfill
[fi
]=0;
686 fseek(f
[fi
],offset
[fi
],SEEK_SET
);
687 if(length
[fi
]!=-1)bytesleft
[fi
]=length
[fi
]*channels
[fi
]*((bits
[fi
]+7)/8);
691 eof_all
=input_read();
694 if(acc_loop
&& !acc_rewind
){
709 for(fi
=0;fi
<inputs
;fi
++){
710 if(blockbufferfill
[fi
]){
711 for(i
=ch
;i
<ch
+channels
[fi
];i
++){
713 float *data
=blockbuffer
[i
];
715 /* window the blockbuffer into the FFT buffer */
716 for(j
=0;j
<blocksize
;j
++){
717 freqbuffer
[j
]=data
[j
]*window
[j
];
723 pthread_mutex_lock(&feedback_mutex
);
725 /* perform desired accumulations */
726 for(j
=0;j
<blocksize
+2;j
+=2){
727 float R
= freqbuffer
[j
];
728 float I
= freqbuffer
[j
+1];
734 floor_yy
[i
][j
>>1]+=sqM
*sqM
;
735 floor_y
[i
][j
>>1]+=sqM
;
738 /* deal with phase accumulate/rotate */
740 /* normalize/store ref for later rotation */
742 process_work
[j
+1] = -I
;
745 /* rotate signed square phase according to ref for phase calculation */
748 float rR
= process_work
[j
];
749 float rI
= process_work
[j
+1];
754 ph_instant
[i
][j
+1]=pI
;
759 if(feedback_max
[i
][j
>>1]<sqM
){
765 feedback_instant
[i
][j
>>1]=sqM
;
766 feedback_acc
[i
][j
>>1]+=sqM
;
768 if(feedback_max
[i
][j
>>1]<sqM
)
769 feedback_max
[i
][j
>>1]=sqM
;
774 pthread_mutex_unlock(&feedback_mutex
);
781 feedback_increment
++;
782 write(eventpipe
[1],"",1);
786 void *process_thread(void *dummy
){
787 while(!process_exit
&& process());
789 write(eventpipe
[1],"",1);
793 void process_dump(int mode
){
798 out
=fopen("accumulate.m","w");
800 for(fi
=0;fi
<inputs
;fi
++){
801 for(i
=0;i
<blocksize
/2+1;i
++){
802 fprintf(out
,"%f ",(double)i
*rate
[fi
]/blocksize
);
804 for(j
=ch
;j
<ch
+channels
[fi
];j
++)
805 fprintf(out
,"%f ",todB(feedback_acc
[j
][i
])*.5);
815 out
=fopen("max.m","w");
817 for(fi
=0;fi
<inputs
;fi
++){
818 for(i
=0;i
<blocksize
/2+1;i
++){
819 fprintf(out
,"%f ",(double)i
*rate
[fi
]/blocksize
);
821 for(j
=ch
;j
<ch
+channels
[fi
];j
++)
822 fprintf(out
,"%f ",todB(feedback_max
[j
][i
])*.5);
832 out
=fopen("instant.m","w");
834 for(fi
=0;fi
<inputs
;fi
++){
835 for(i
=0;i
<blocksize
/2+1;i
++){
836 fprintf(out
,"%f ",(double)i
*rate
[fi
]/blocksize
);
838 for(j
=ch
;j
<ch
+channels
[fi
];j
++)
839 fprintf(out
,"%f ",todB(feedback_instant
[j
][i
])*.5);
849 out
=fopen("accphase.m","w");
851 for(fi
=0;fi
<inputs
;fi
++){
854 for(i
=0;i
<blocksize
+2;i
+=2){
855 fprintf(out
,"%f ",(double)i
*.5*rate
[fi
]/blocksize
);
856 fprintf(out
,"%f ",atan2(ph_acc
[ch
+1][i
+1],ph_acc
[ch
+1][i
])*57.29);
867 void clear_noise_floor(){
869 for(i
=0;i
<total_ch
;i
++){
870 memset(floor_y
[i
],0,(blocksize
/2+1)*sizeof(**floor_y
));
871 memset(floor_yy
[i
],0,(blocksize
/2+1)*sizeof(**floor_yy
));
876 /* how many bins to 'trim' off the edge of calculated data when we
877 know we've hit a boundary of marginal measurement */
880 float **process_fetch(int res
, int scale
, int mode
, int link
,
881 int *active
, int width
,
882 float *ymax
, float *pmax
, float *pmin
,
883 float **yfloor
,int noise
){
890 /* are our scale mappings up to date? */
891 if(res
!= metares
|| scale
!= metascale
|| width
!= metawidth
){
892 if(!xmappingL
) xmappingL
= calloc(inputs
, sizeof(*xmappingL
));
893 if(!xmappingH
) xmappingH
= calloc(inputs
, sizeof(*xmappingH
));
897 work_floor
= calloc(total_ch
,sizeof(*work_floor
));
898 for(i
=0;i
<total_ch
;i
++){
900 work_floor
[i
] = realloc(work_floor
[i
],(width
+1)*sizeof(**work_floor
));
902 work_floor
[i
] = calloc((width
+1),sizeof(**work_floor
));
905 for(fi
=0;fi
<inputs
;fi
++){
907 /* if mapping preexists, resize it */
909 xmappingL
[fi
] = realloc(xmappingL
[fi
],(width
+1)*sizeof(**xmappingL
));
911 xmappingL
[fi
] = malloc((width
+1)*sizeof(**xmappingL
));
914 xmappingH
[fi
] = realloc(xmappingH
[fi
],(width
+1)*sizeof(**xmappingH
));
916 xmappingH
[fi
] = malloc((width
+1)*sizeof(**xmappingH
));
924 /* generate new numbers */
925 for(i
=0;i
<width
;i
++){
932 case 0: /* screen-resolution */
935 case 1: /* 1/24th octave */
936 loff
= .95918945710913818816;
937 hoff
= 1.04254690518999138632;
939 case 2: /* 1/12th octave */
940 loff
= .94387431268169349664;
941 hoff
= 1.05946309435929526455;
943 case 3: /* 1/3th octave */
944 loff
= .79370052598409973738;
945 hoff
= 1.25992104989487316475;
951 lfreq
= pow(10.,(i
-off
)/(width
-1)
952 * (log10(100000.)-log10(5.))
954 hfreq
= pow(10.,(i
+off
)/(width
-1)
955 * (log10(100000.)-log10(5.))
959 lfreq
= pow(2.,(i
-off
)/(width
-1)
960 * (log2(20000.)-log2(25.))
962 hfreq
= pow(2.,(i
+off
)/(width
-1)
963 * (log2(20000.)-log2(25.))
966 case 2: /* screen-resolution linear */
967 lfreq
=(i
-off
)*20000./(width
-1)*loff
;
968 hfreq
=(i
+off
)*20000./(width
-1)*hoff
;
972 xmappingL
[fi
][i
]=lfreq
/(rate
[fi
]*.5)*(blocksize
/2);
973 xmappingH
[fi
][i
]=hfreq
/(rate
[fi
]*.5)*(blocksize
/2);
977 for(i
=0;i
<width
;i
++){
978 if(xmappingL
[fi
][i
]<0.)xmappingL
[fi
][i
]=0.;
979 if(xmappingL
[fi
][i
]>blocksize
/2.)xmappingL
[fi
][i
]=blocksize
/2.;
980 if(xmappingH
[fi
][i
]<0.)xmappingH
[fi
][i
]=0.;
981 if(xmappingH
[fi
][i
]>blocksize
/2.)xmappingH
[fi
][i
]=blocksize
/2.;
985 for(i
=0;i
<total_ch
;i
++)
987 plot_data
[i
] = realloc(plot_data
[i
],(width
+1)*sizeof(**plot_data
));
989 plot_data
[i
] = malloc((width
+1)*sizeof(**plot_data
));
993 /* 'illustrate' the noise floor */
996 plot_floor
=realloc(plot_floor
,(width
+1)*sizeof(*plot_floor
));
998 plot_floor
=calloc((width
+1),sizeof(*plot_floor
));
1000 if(metanoise
!=link
){
1001 float *y
= plot_floor
;
1004 for(i
=0;i
<width
;i
++)
1007 for(fi
=0;fi
<inputs
;fi
++){
1008 float *L
= xmappingL
[fi
];
1009 float *H
= xmappingH
[fi
];
1010 float d
= 1./floor_count
;
1012 for(ci
=0;ci
<channels
[fi
];ci
++){
1013 float *fy
= floor_y
[ci
+ch
];
1014 float *fyy
= floor_yy
[ci
+ch
];
1015 float *w
= work_floor
[ci
+ch
];
1017 for(i
=0;i
<width
;i
++){
1018 int first
=floor(L
[i
]);
1019 int last
=floor(H
[i
]);
1022 float v
= fyy
[first
]*floor_count
- fy
[first
]*fy
[first
];
1025 float del
=H
[i
]-L
[i
];
1029 float del
=1.-(L
[i
]-first
);
1033 for(j
=first
+1;j
<last
;j
++){
1034 v
= fyy
[j
]*floor_count
- fy
[j
]*fy
[j
];
1039 v
= fyy
[last
]*floor_count
- fy
[last
]*fy
[last
];
1044 vsum
= 10*sqrt(vsum
)*d
;
1046 w
[i
] = esum
+vsum
*10;
1047 esum
= todB_a(w
+i
)*.5;
1049 if(esum
>y
[i
])y
[i
]=esum
;
1055 if(link
== LINK_INDEPENDENT
&& mode
==0)
1058 for(i
=0;i
<total_ch
;i
++)
1059 memset(work_floor
[i
],0,width
*sizeof(**work_floor
));
1063 /* mode selects the base data set */
1065 case 0: /* independent / instant */
1066 data
=feedback_instant
;
1069 case 1: /* independent / max */
1083 for(fi
=0;fi
<inputs
;fi
++){
1084 float *L
= xmappingL
[fi
];
1085 float *H
= xmappingH
[fi
];
1088 case LINK_INDEPENDENT
:
1090 for(ci
=0;ci
<channels
[fi
];ci
++){
1091 float *y
= plot_data
[ci
+ch
];
1092 float *m
= data
[ci
+ch
];
1094 for(i
=0;i
<width
;i
++){
1095 int first
=floor(L
[i
]);
1096 int last
=floor(H
[i
]);
1100 float del
=H
[i
]-L
[i
];
1103 float del
=1.-(L
[i
]-first
);
1106 for(j
=first
+1;j
<last
;j
++)
1113 sum
=todB_a(&sum
)*.5;
1114 if(sum
>*ymax
)*ymax
=sum
;
1123 float *y
= plot_data
[ch
];
1124 memset(y
,0,(width
+1)*sizeof(*y
));
1126 for(ci
=0;ci
<channels
[fi
];ci
++){
1127 float *m
= data
[ci
+ch
];
1129 for(i
=0;i
<width
;i
++){
1130 int first
=floor(L
[i
]);
1131 int last
=floor(H
[i
]);
1134 float del
=H
[i
]-L
[i
];
1137 float del
=1.-(L
[i
]-first
);
1140 for(j
=first
+1;j
<last
;j
++)
1150 for(i
=0;i
<width
;i
++){
1151 float sum
=todB_a(y
+i
)*.5;
1152 if(sum
>*ymax
)*ymax
=sum
;
1160 float *y
= plot_data
[ch
];
1162 for(i
=0;i
<width
;i
++)
1165 for(ci
=0;ci
<channels
[fi
];ci
++){
1166 float *m
= data
[ci
+ch
];
1167 if(ci
==0 || active
[ch
+ci
]){
1168 for(i
=0;i
<width
;i
++){
1169 int first
=floor(L
[i
]);
1170 int last
=floor(H
[i
]);
1174 float del
=H
[i
]-L
[i
];
1177 float del
=1.-(L
[i
]-first
);
1180 for(j
=first
+1;j
<last
;j
++)
1196 for(i
=0;i
<width
;i
++){
1197 float v
= (y
[i
]>0?y
[i
]:0);
1198 float sum
=todB_a(&v
)*.5;
1199 if(sum
>*ymax
)*ymax
=sum
;
1207 float *r
= plot_data
[ch
];
1208 for(ci
=0;ci
<channels
[fi
];ci
++){
1209 float *y
= plot_data
[ch
+ci
];
1210 float *m
= data
[ci
+ch
];
1211 if(ci
==0 || active
[ch
+ci
]){
1212 for(i
=0;i
<width
;i
++){
1213 int first
=floor(L
[i
]);
1214 int last
=floor(H
[i
]);
1218 float del
=H
[i
]-L
[i
];
1221 float del
=1.-(L
[i
]-first
);
1224 for(j
=first
+1;j
<last
;j
++)
1234 sum
=(r
[i
]>sum
?0.f
:sum
-r
[i
]);
1235 y
[i
]=todB_a(&sum
)*.5;
1236 if(y
[i
]>*ymax
)*ymax
=y
[i
];
1244 case LINK_IMPEDENCE_p1
:
1245 case LINK_IMPEDENCE_1
:
1246 case LINK_IMPEDENCE_10
:
1248 float shunt
= (link
== LINK_IMPEDENCE_p1
?.1:(link
== LINK_IMPEDENCE_1
?1:10));
1249 float *r
= plot_data
[ch
];
1251 for(ci
=0;ci
<channels
[fi
];ci
++){
1252 float *y
= plot_data
[ci
+ch
];
1253 float *m
= data
[ch
+ci
];
1255 if(ci
==0 || active
[ch
+ci
]){
1256 for(i
=0;i
<width
;i
++){
1257 int first
=floor(L
[i
]);
1258 int last
=floor(H
[i
]);
1262 float del
=H
[i
]-L
[i
];
1265 float del
=1.-(L
[i
]-first
);
1268 for(j
=first
+1;j
<last
;j
++)
1276 /* stash the reference in the work vector */
1280 /* 'r' collected at source, 'sum' across the shunt */
1284 if(S
>(1e-5) && V
>S
){
1285 y
[i
] = shunt
*(V
-S
)/S
;
1294 /* scan the resulting buffers for marginal data that would
1295 produce spurious output. Specifically we look for sharp
1296 falloffs of > 40dB or an original test magnitude under
1300 for(i
=0;i
<width
;i
++){
1301 float v
= r
[i
] = todB_a(r
+i
)*.5;
1305 for(ci
=1;ci
<channels
[fi
];ci
++){
1307 float *y
= plot_data
[ci
+ch
];
1308 for(i
=0;i
<width
;i
++){
1309 if(r
[i
]<max
-40 || r
[i
]<-70){
1315 if(r
[j
]>max
-40 && r
[j
]>-70)break;
1319 for(;j
<i
&& j
<width
;j
++){
1323 if(!isnan(y
[i
]) && y
[i
]>*ymax
)*ymax
= y
[i
];
1331 case LINK_PHASE
: /* response/phase */
1333 if(channels
[fi
]>=2){
1334 float *om
= plot_data
[ch
];
1335 float *op
= plot_data
[ch
+1];
1337 float *r
= data
[ch
];
1338 float *rn
= work_floor
[ch
];
1339 float *m
= data
[ch
+1];
1340 float *mn
= work_floor
[ch
+1];
1341 float *p
= ph
[ch
+1];
1344 if(feedback_count
[ch
]==0){
1345 memset(om
,0,width
*sizeof(*om
));
1346 memset(op
,0,width
*sizeof(*op
));
1348 /* two vectors only; response and phase */
1350 if(active
[ch
] || active
[ch
+1]){
1351 for(i
=0;i
<width
;i
++){
1352 int first
=floor(L
[i
]);
1353 int last
=floor(H
[i
]);
1357 float del
=H
[i
]-L
[i
];
1361 float del
=1.-(L
[i
]-first
);
1365 for(j
=first
+1;j
<last
;j
++){
1375 if(sumR
>rn
[i
] && sumM
>mn
[i
]){
1376 mag
[i
] = todB_a(&sumR
)*.5;
1378 om
[i
] = todB_a(&sumM
)*.5;
1387 for(i
=0;i
<width
;i
++){
1388 int first
=floor(L
[i
]);
1389 int last
=floor(H
[i
]);
1393 float del
=H
[i
]-L
[i
];
1394 sumR
=p
[(first
<<1)]*del
;
1395 sumI
=p
[(first
<<1)+1]*del
;
1397 float del
=1.-(L
[i
]-first
);
1398 sumR
=p
[(first
<<1)]*del
;
1399 sumI
=p
[(first
<<1)+1]*del
;
1401 for(j
=first
+1;j
<last
;j
++){
1407 sumR
+=p
[(last
<<1)]*del
;
1408 sumI
+=p
[(last
<<1)+1]*del
;
1412 op
[i
] = atan2(sumI
,sumR
)*57.29;
1419 /* scan the resulting buffers for marginal data that would
1420 produce spurious output. Specifically we look for sharp
1421 falloffs of > 40dB or an original test magnitude under
1423 if(active
[ch
] || active
[ch
+1]){
1424 for(i
=0;i
<width
;i
++){
1433 if(!isnan(om
[j
]))break;
1438 for(;j
<i
&& j
<width
;j
++){
1443 if(om
[i
]>*ymax
)*ymax
= om
[i
];
1444 if(op
[i
]>*pmax
)*pmax
= op
[i
];
1445 if(op
[i
]<*pmin
)*pmin
= op
[i
];
1453 case LINK_THD
: /* THD */
1454 case LINK_THD2
: /* THD-2 */
1455 case LINK_THDN
: /* THD+N */
1456 case LINK_THDN2
: /* THD+N-2 */