[xiph-commits] r18229 - trunk/spectrum
xiphmont at svn.xiph.org
xiphmont at svn.xiph.org
Thu Apr 12 07:29:17 PDT 2012
Author: xiphmont
Date: 2012-04-12 07:29:17 -0700 (Thu, 12 Apr 2012)
New Revision: 18229
Added:
trunk/spectrum/spec_panel.c
trunk/spectrum/spec_plot.c
trunk/spectrum/spec_plot.h
trunk/spectrum/spec_process.c
trunk/spectrum/spectrum.c
trunk/spectrum/wave_panel.c
trunk/spectrum/wave_plot.c
trunk/spectrum/wave_plot.h
trunk/spectrum/wave_process.c
trunk/spectrum/waveform.c
trunk/spectrum/waveform.h
Removed:
trunk/spectrum/main.c
trunk/spectrum/panel.c
trunk/spectrum/plot.h
trunk/spectrum/process.c
Modified:
trunk/spectrum/Makefile
trunk/spectrum/analyzer.h
trunk/spectrum/io.c
trunk/spectrum/io.h
trunk/spectrum/version.h
Log:
Get the beginnings of the simple waveform viewer in
Modified: trunk/spectrum/Makefile
===================================================================
--- trunk/spectrum/Makefile 2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/Makefile 2012-04-12 14:29:17 UTC (rev 18229)
@@ -25,18 +25,23 @@
ETCDIR=/etc/spectrum
MANDIR=$(PREFIX)/man
-SRC = main.c process.c panel.c plot.c io.c
-OBJ = main.o process.o panel.o plot.o io.o
+SRC = spectrum.c waveform.c spec_process.c wave_process.c spec_panel.c wave_panel.c spec_plot.c wave_plot.c io.c
+SPECTRUM_OBJ = spectrum.o spec_process.o spec_panel.o spec_plot.o io.o
+WAVEFORM_OBJ = waveform.o wave_process.o wave_panel.o wave_plot.o io.o
+OBJ = $(SPECTRUM_OBJ) $(WAVEFORM_OBJ)
+
GCF = -DETCDIR=\\\"$(ETCDIR)\\\" `pkg-config --cflags gtk+-2.0`
+CFLAGS := ${CFLAGS} $(GCF) $(ADD_DEF)
+
all:
- $(MAKE) target CFLAGS="-O3 -ffast-math -fomit-frame-pointer $(GCF) $(ADD_DEF)"
+ $(MAKE) target CFLAGS="${CFLAGS} -O3 -ffast-math -fomit-frame-pointer"
debug:
- $(MAKE) target CFLAGS="-g -Wall -W -Wno-unused-parameter -D__NO_MATH_INLINES $(GCF) $(ADD_DEF)"
+ $(MAKE) target CFLAGS="${CFLAGS} -g -Wall -W -Wno-unused-parameter -D__NO_MATH_INLINES"
profile:
- $(MAKE) target CFLAGS="-pg -g -O3 -ffast-math $(GCF) $(ADD_DEF)" LIBS="-lgprof-helper "
+ $(MAKE) target CFLAGS="${CFLAGS} -pg -g -O3 -ffast-math" LIBS="-lgprof-helper "
clean:
rm -f $(OBJ) *.d *.d.* gmon.out spectrum
@@ -55,15 +60,23 @@
include $(SRC:.c=.d)
endif
-target: $(OBJ) spectrum-wisdomrc
+spectrum: $(SPECTRUM_OBJ) spectrum-wisdomrc
./touch-version
- $(LD) $(OBJ) $(CFLAGS) -o spectrum $(LIBS) `pkg-config --libs gtk+-2.0` -lpthread -lfftw3f -lm
+ $(LD) $(SPECTRUM_OBJ) -o spectrum $(LIBS) $(CFLAGS) `pkg-config --libs gtk+-2.0` -lpthread -lfftw3f -lm
+waveform: $(WAVEFORM_OBJ)
+ ./touch-version
+ $(LD) $(WAVEFORM_OBJ) -o waveform $(LIBS) $(CFLAGS) `pkg-config --libs gtk+-2.0` -lpthread -lm
+
+target: spectrum waveform
+
install: target
$(INSTALL) -d -m 0755 $(BINDIR)
$(INSTALL) -m 0755 spectrum $(BINDIR)
+ $(INSTALL) -m 0755 waveform $(BINDIR)
$(INSTALL) -d -m 0755 $(ETCDIR)
$(INSTALL) -m 0644 spectrum-gtkrc $(ETCDIR)
+ $(INSTALL) -m 0644 waveform-gtkrc $(ETCDIR)
$(INSTALL) -m 0644 spectrum-wisdomrc $(ETCDIR)
# $(INSTALL) -d -m 0755 $(MANDIR)
# $(INSTALL) -d -m 0755 $(MANDIR)/man1
Modified: trunk/spectrum/analyzer.h
===================================================================
--- trunk/spectrum/analyzer.h 2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/analyzer.h 2012-04-12 14:29:17 UTC (rev 18229)
@@ -91,17 +91,6 @@
float *ymax, float *pmax, float *pmin,
float **floor, int noise);
-extern int inputs;
-extern int total_ch;
-extern int bits[MAX_FILES];
-extern int bigendian[MAX_FILES];
-extern int channels[MAX_FILES];
-extern int rate[MAX_FILES];
-extern int signedp[MAX_FILES];
-extern char *inputname[MAX_FILES];
-extern int seekable[MAX_FILES];
-extern int global_seekable;
-
extern pthread_mutex_t feedback_mutex;
extern int feedback_increment;
extern float **feedback_acc;
Modified: trunk/spectrum/io.c
===================================================================
--- trunk/spectrum/io.c 2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/io.c 2012-04-12 14:29:17 UTC (rev 18229)
@@ -23,9 +23,15 @@
#include "io.h"
+int bits[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+int bigendian[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+int channels[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+int rate[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+int signedp[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+
extern sig_atomic_t acc_loop;
extern int blocksize;
-static int blockslice[MAX_FILES]= {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+int blockslice[MAX_FILES]= {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
float **blockbuffer=0;
int blockbufferfill[MAX_FILES]={0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
@@ -371,28 +377,17 @@
}
- /* select the full-block slice size: ~10fps */
- blockslice[fi]=rate[fi]/10;
- while(blockslice[fi]>blocksize/2)blockslice[fi]/=2;
+ if(length[fi]!=-1)
+ bytesleft[fi]=length[fi]*channels[fi]*((bits[fi]+7)/8);
total_ch += channels[fi];
- if(length[fi]!=-1)
- bytesleft[fi]=length[fi]*channels[fi]*((bits[fi]+7)/8);
-
}else{
fprintf(stderr,"Unable to open %s: %s\n",inputname[fi],strerror(errno));
exit(1);
}
}
- blockbuffer=malloc(total_ch*sizeof(*blockbuffer));
-
- for(i=0;i<total_ch;i++){
- blockbuffer[i]=calloc(blocksize,sizeof(**blockbuffer));
- }
-
return 0;
-
}
/* Convert new data from readbuffer into the blockbuffers until the
@@ -506,23 +501,33 @@
loop unset: return EOF if all streams have hit EOF
pad individual EOF streams out with zeroes until global EOF is hit */
-int input_read(void){
- int i,fi,ch=0;
+int input_read(int loop, int partialok){
+ int i,j,fi,ch=0;
int eof=1;
int notdone=1;
+ int rewound[total_ch];
+ memset(rewound,0,sizeof(rewound));
+ if(blockbuffer==0){
+ blockbuffer=malloc(total_ch*sizeof(*blockbuffer));
+
+ for(i=0;i<total_ch;i++){
+ blockbuffer[i]=calloc(blocksize,sizeof(**blockbuffer));
+ }
+ }
+
for(fi=0;fi<inputs;fi++){
-
/* shift according to slice */
- if(blockbufferfill[fi]==blocksize){
- if(blockslice[fi]<blocksize){
- for(i=0;i<channels[fi];i++)
- memmove(blockbuffer[i+ch],blockbuffer[i+ch]+blockslice[fi],
- (blocksize-blockslice[fi])*sizeof(**blockbuffer));
- blockbufferfill[fi]-=blockslice[fi];
- }else
- blockbufferfill[fi]=0;
- }
+ if(blockslice[fi]<blocksize)
+ for(i=ch;i<channels[fi]+ch;i++)
+ memmove(blockbuffer[i],blockbuffer[i]+blockslice[fi],
+ (blocksize-blockslice[fi])*sizeof(**blockbuffer));
+ blockbufferfill[fi]-=blockslice[fi];
+ if(blockbufferfill[fi]<0)
+ blockbufferfill[fi]=0;
+ for(i=ch;i<channels[fi]+ch;i++)
+ for(j=blockbufferfill[fi];j<blocksize;j++)
+ blockbuffer[i][j]=NAN;
ch+=channels[fi];
}
@@ -531,11 +536,11 @@
/* if there's data left to be pulled out of a readbuffer, do that */
LBEconvert();
-
+
ch=0;
for(fi=0;fi<inputs;fi++){
if(blockbufferfill[fi]!=blocksize){
-
+
/* shift the read buffer before fill if there's a fractional
frame in it */
if(readbufferptr[fi]!=readbufferfill[fi] && readbufferptr[fi]>0){
@@ -547,39 +552,44 @@
readbufferfill[fi]=0;
readbufferptr[fi]=0;
}
-
+
/* attempt to top off the readbuffer */
{
long actually_readbytes=0,readbytes=readbuffersize-readbufferfill[fi];
if(readbytes>0)
actually_readbytes=fread(readbuffer[fi]+readbufferfill[fi],1,readbytes,f[fi]);
-
- if(actually_readbytes<0){
- fprintf(stderr,"Input read error from %s: %s\n",inputname[fi],strerror(errno));
+
+ if(actually_readbytes<=0 && ferror(f[fi])){
+ fprintf(stderr,"Input read error from %s: %s\n",
+ inputname[fi],strerror(ferror(f[fi])));
}else if (actually_readbytes==0){
- /* don't process any partially-filled blocks; the
- stairstep at the end could pollute results badly */
-
- memset(readbuffer[fi],0,readbuffersize);
- bytesleft[fi]=0;
- readbufferfill[fi]=0;
- readbufferptr[fi]=0;
- blockbufferfill[fi]=0;
-
+ /* real, hard EOF/error in a partially filled block */
+ if(!partialok){
+ /* partial frame is *not* ok. zero it out. */
+ memset(readbuffer[fi],0,readbuffersize);
+ bytesleft[fi]=0;
+ readbufferfill[fi]=0;
+ readbufferptr[fi]=0;
+ blockbufferfill[fi]=0;
+ }
+ if(loop && (!rewound[fi] || (partialok && blockbufferfill[fi]))){
+ /* rewind this file and continue */
+ fseek(f[fi],offset[fi],SEEK_SET);
+ if(length[fi]!=-1)
+ bytesleft[fi]=length[fi]*channels[fi]*((bits[fi]+7)/8);
+ notdone=1;
+ rewound[fi]=1;
+ }
}else{
+ /* got a read */
bytesleft[fi]-=actually_readbytes;
readbufferfill[fi]+=actually_readbytes;
-
- /* conditionally clear global EOF */
- if(acc_loop){
- if(seekable[fi])eof=0;
- }else{
- eof=0;
- }
- notdone=1;
+ notdone=1;
}
}
+ }else{
+ eof=0;
}
ch += channels[fi];
}
Modified: trunk/spectrum/io.h
===================================================================
--- trunk/spectrum/io.h 2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/io.h 2012-04-12 14:29:17 UTC (rev 18229)
@@ -47,7 +47,7 @@
#define readbuffersize 8192
extern int input_load(void);
-extern int input_read(void);
+extern int input_read(int loop_p, int partial_p);
extern int rewind_files(void);
extern int inputs;
@@ -61,6 +61,7 @@
extern int seekable[MAX_FILES];
extern int global_seekable;
+extern int blockslice[MAX_FILES];
extern int blockbufferfill[MAX_FILES];
extern float **blockbuffer;
#endif
Deleted: trunk/spectrum/main.c
===================================================================
--- trunk/spectrum/main.c 2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/main.c 2012-04-12 14:29:17 UTC (rev 18229)
@@ -1,292 +0,0 @@
-/*
- *
- * gtk2 spectrum analyzer
- *
- * Copyright (C) 2004 Monty
- *
- * This analyzer is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * The analyzer is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Postfish; see the file COPYING. If not, write to the
- * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
- */
-
-#include "analyzer.h"
-#include <signal.h>
-#include <getopt.h>
-#include <fenv.h> // Thank you C99!
-#include <fftw3.h>
-#include <gtk/gtk.h>
-#include "version.h"
-
-int eventpipe[2];
-char *version;
-char *inputname[MAX_FILES];
-int inputs=0;
-int blocksize = 131072;
-
-int bits[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
-int bigendian[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
-int channels[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
-int rate[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
-int signedp[MAX_FILES] = {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
-
-void handler(int sig){
- signal(sig,SIG_IGN);
- if(sig!=SIGINT){
- fprintf(stderr,
- "\nTrapped signal %d; exiting!\n"
- "This signal almost certainly indicates a bug in the analyzer;\n"
- "If this version of the analyzer is newer than a few months old,\n"
- "please email a detailed report of the crash along with\n"
- "processor type, OS version, FFTW3 version, and as much\n"
- "information as possible about what caused the crash. The best\n"
- "possible report will outline the exact steps needed to\n"
- "reproduce the error, ensuring that I can fix the bug as\n"
- "quickly as possible.\n\n"
- "-- monty at xiph.org, spectrum revision %s\n\n",sig,version);
- }
-
- gtk_main_quit();
-}
-
-const char *optstring = "-r:c:EeBlb:suhF:";
-
-struct option options [] = {
- {"rate",required_argument,NULL,'r'},
- {"channels",required_argument,NULL,'c'},
- {"big-endian",no_argument,NULL,'E'},
- {"little-endian",no_argument,NULL,'e'},
- {"bits",required_argument,NULL,'b'},
- {"signed",no_argument,NULL,'s'},
- {"unsigned",no_argument,NULL,'u'},
- {"help",no_argument,NULL,'h'},
- {"fft-size",required_argument,NULL,'h'},
-
- {NULL,0,NULL,0}
-};
-
-static void usage(FILE *f){
- fprintf( f,
-"\ngtk2 spectrum analyzer, revision %s\n\n"
-
-"USAGE:\n\n"
-" spectrum [options] [file]\n\n"
-
-"OPTIONS:\n\n"
-" -b --bits <bits> : Force input to be read as 8, 16, 24 or 32 bit\n"
-" PCM. Default bit depth is normally read from\n"
-" the file/stream header or set to 16 bits\n"
-" for raw input.\n"
-" -B -E --big-endian : Force input to be read as big endian.\n"
-" Default endianness is normally read from the\n"
-" file/stream header or set to host"
-" endianness for raw input.\n"
-" -c --channels <channels> : Input channel number override; use to\n"
-" specify the number of channels in a raw\n"
-" input. default: 1\n"
-" -e -l --little-endian : Force input to be read as little endian.\n"
-" Default endianness is normally read from the\n"
-" file/stream header or set to host"
-" endianness for raw input.\n"
-" -F --fft-size : Set the size of the fft transform used. Valid\n"
-" range 8192 to 262144, 131072 default.\n"
-" -h --help : print this help\n"
-" -r --rate <Hz> : Input sample rate override in Hz; use to\n"
-" specify the rate of a raw input.\n"
-" default: 44100\n"
-" -s --signed : Force input to be read as signed PCM.\n"
-" Signedness is normally read from the \n"
-" file/stream header or set to signed for raw\n"
-" input.\n"
-" -u --unsigned : Force input to be read as unsigned PCM.\n"
-" Signedness is normally read from the \n"
-" file/stream header or set to signed for raw\n"
-" input.\n\n"
-
-"INPUT:\n\n"
-
-" Spectrum takes raw, WAV or AIFF input either from stdin or from \n"
-" file[s]/stream[s] specified on the command line.\n\n",version);
-
-}
-
-void parse_command_line(int argc, char **argv){
- int c,long_option_index;
-
- while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
- switch(c){
- case 1:
- /* file name that belongs to current group */
- if(inputs>=MAX_FILES){
- fprintf(stderr,"Maximum of MAX_FILES input files exceeded. Oops. Programmer was lazy.\n\n");
- exit(1);
- }
- inputname[inputs++]=strdup(optarg);
- break;
- case 'b':
- /* force bit width */
- {
- int a=atoi(optarg);
- bits[inputs]=a;
- if(a!=8 && a!=16 && a!=24 && a!=32){
- usage(stderr);
- exit(1);
- }
- }
- break;
- case 'F':
- blocksize = atoi(optarg);
- if(blocksize<8192 || blocksize>262144){
- usage(stderr);
- exit(1);
- }
- break;
- case 'B':case 'E':
- /* force big endian */
- bigendian[inputs]=1;
- break;
- case 'c':
- /* force channels */
- {
- int a=atoi(optarg);
- channels[inputs]=a;
- if(a<1 || a>32){
- usage(stderr);
- exit(1);
- }
- }
- break;
- case 'l':case 'e':
- /* force little endian */
- bigendian[inputs]=0;
- break;
- case 'h':
- usage(stdout);
- exit(0);
- case 'r':
- /* force rate */
- {
- int a=atoi(optarg);
- rate[inputs]=a;
- if(a<4000 || a>200000){
- usage(stderr);
- exit(1);
- }
- }
- break;
- case 's':
- /* force signed */
- signedp[inputs]=1;
- break;
- case 'u':
- /* force unsigned */
- signedp[inputs]=0;
- break;
- default:
- usage(stderr);
- exit(0);
- }
- }
-}
-
-static int sigill=0;
-void sigill_handler(int sig){
- /* make sure */
- if(sig==SIGILL)sigill=1;
-}
-
-int main(int argc, char **argv){
-
- version=strstr(VERSION,"version.h");
- if(version){
- char *versionend=strchr(version,' ');
- if(versionend)versionend=strchr(versionend+1,' ');
- if(versionend)versionend=strchr(versionend+1,' ');
- if(versionend)versionend=strchr(versionend+1,' ');
- if(versionend){
- int len=versionend-version-9;
- version=strdup(version+10);
- version[len-1]=0;
- }
- }else{
- version="";
- }
-
- /* parse command line and open all the input files */
- parse_command_line(argc, argv);
-
- /* We do not care about FPEs; rather, underflow is nominal case, and
- its better to ignore other traps in production than to crash the
- app. Please inform the FPU of this. */
-
-#ifndef DEBUG
- fedisableexcept(FE_INVALID);
- fedisableexcept(FE_INEXACT);
- fedisableexcept(FE_UNDERFLOW);
- fedisableexcept(FE_OVERFLOW);
-#else
- feenableexcept(FE_INVALID);
- feenableexcept(FE_INEXACT);
- feenableexcept(FE_UNDERFLOW);
- feenableexcept(FE_OVERFLOW);
-#endif
-
- /* Linux Altivec support has a very annoying problem; by default,
- math on denormalized floats will simply crash the program. FFTW3
- uses Altivec, so boom, but only random booms.
-
- By the C99 spec, the above exception configuration is also
- supposed to handle Altivec config, but doesn't. So we use the
- below ugliness to both handle altivec and non-alitvec PPC. */
-
-#ifdef __PPC
-#include <altivec.h>
- signal(SIGILL,sigill_handler);
-
-#if (defined __GNUC__) && (__GNUC__ == 3) && ! (defined __APPLE_CC__)
- __vector unsigned short noTrap =
- (__vector unsigned short){0,0,0,0,0,0,0x1,0};
-#else
- vector unsigned short noTrap =
- (vector unsigned short)(0,0,0,0,0,0,0x1,0);
-#endif
-
- vec_mtvscr(noTrap);
-#endif
-
- /* easiest way to inform gtk of changes and not deal with locking
- issues around the UI */
- if(pipe(eventpipe)){
- fprintf(stderr,"Unable to open event pipe:\n"
- " %s\n",strerror(errno));
-
- exit(1);
- }
-
- /* Allows event compression on the read side */
- if(fcntl(eventpipe[0], F_SETFL, O_NONBLOCK)){
- fprintf(stderr,"Unable to set O_NONBLOCK on event pipe:\n"
- " %s\n",strerror(errno));
-
- exit(1);
- }
-
- //signal(SIGINT,handler);
- signal(SIGSEGV,handler);
-
- if(input_load())exit(1);
- panel_go(argc,argv);
-
- return(0);
-}
Deleted: trunk/spectrum/panel.c
===================================================================
--- trunk/spectrum/panel.c 2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/panel.c 2012-04-12 14:29:17 UTC (rev 18229)
@@ -1,840 +0,0 @@
-/*
- *
- * gtk2 spectrum analyzer
- *
- * Copyright (C) 2004 Monty
- *
- * This analyzer is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * The analyzer is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Postfish; see the file COPYING. If not, write to the
- * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
- */
-
-#include "analyzer.h"
-#include <gtk/gtk.h>
-#include <gdk/gdkkeysyms.h>
-#include "fisharray.h"
-#include "plot.h"
-
-sig_atomic_t increment_fish=0;
-
-static struct panel {
- GtkWidget *twirlimage;
- GdkPixmap *ff[19];
- GdkBitmap *fb[19];
-
- GtkAccelGroup *group;
- GtkWidget *toplevel;
-
-
- guint fishframe_timer;
- int fishframe_init;
- int fishframe;
-
- GtkWidget *plot;
- GtkWidget *run;
- GtkWidget **chbuttons;
-
-} p;
-
-int plot_res=0;
-int plot_scale=0;
-int plot_mode=0;
-int plot_link=0;
-int plot_hold=0;
-int plot_depth=90;
-int plot_noise=0;
-int plot_last_update=0;
-int *active;
-
-static void replot(struct panel *p){
- int i,lactive[total_ch];
- for(i=0;i<total_ch;i++)
- lactive[i]=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p->chbuttons[i]));
-
- /* update the spectral display; send new data */
- if(!plot_hold){
- pthread_mutex_lock(&feedback_mutex);
- plot_refresh(PLOT(p->plot),lactive);
- plot_last_update=feedback_increment;
- pthread_mutex_unlock(&feedback_mutex);
- }
-}
-
-static void shutdown(void){
- gtk_main_quit();
-}
-
-/* gotta have the Fucking Fish */
-static int reanimate_fish(struct panel *p){
- if(process_active || (p->fishframe>0 && p->fishframe<12)){
- /* continue spinning */
- if(increment_fish)p->fishframe++;
- if(p->fishframe>=12)p->fishframe=0;
-
- gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
- p->ff[p->fishframe],
- p->fb[p->fishframe]);
-
- if(p->fishframe==0 && !process_active){
- /* reschedule to blink */
- p->fishframe_timer=
- g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
- return FALSE;
- }
-
- }else{
- p->fishframe++;
- if(p->fishframe<=1)p->fishframe=12;
- if(p->fishframe>=19)p->fishframe=0;
-
- gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
- p->ff[p->fishframe],
- p->fb[p->fishframe]);
-
-
- if(p->fishframe==12){
- /* reschedule to animate */
- p->fishframe_timer=
- g_timeout_add(10,(GSourceFunc)reanimate_fish,p);
- return FALSE;
- }
- if(p->fishframe==0){
- /* reschedule to blink */
- p->fishframe_timer=
- g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
- return FALSE;
- }
- }
- return TRUE;
-}
-
-static void animate_fish(struct panel *p){
- if(p->fishframe_init){
- g_source_remove(p->fishframe_timer);
- p->fishframe_timer=
- g_timeout_add(80,(GSourceFunc)reanimate_fish,p);
- }else{
- p->fishframe_init=1;
- p->fishframe_timer=
- g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
- }
-}
-
-static void dump(GtkWidget *widget,struct panel *p){
- process_dump(plot_mode);
-}
-
-static void noise(GtkWidget *widget,struct panel *p){
- if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))){
- if(plot_noise){
- plot_noise=0;
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),0);
- clear_noise_floor();
- plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,0);
- }else{
- plot_noise=1;
- plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,0);
- }
- }else{
- if(plot_noise){
- gtk_button_set_label(GTK_BUTTON(widget),"clear _noise floor");
- plot_noise=2;
- plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,1);
- }else
- gtk_button_set_label(GTK_BUTTON(widget),"sample _noise floor");
- }
-}
-
-
-static void depthchange(GtkWidget *widget,struct panel *p){
- int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
- switch(choice){
- case 0: /* 10dB */
- plot_depth=10;
- break;
- case 1: /* 20dB */
- plot_depth=20;
- break;
- case 2: /* 45dB */
- plot_depth=45;
- break;
- case 3: /* 90dB */
- plot_depth=90;
- break;
- case 4: /*140dB */
- plot_depth=140;
- break;
- }
- plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
-}
-
-static void set_fg(GtkWidget *c, gpointer in){
- GdkColor *rgb = in;
- gtk_widget_modify_fg(c,GTK_STATE_NORMAL,rgb);
- gtk_widget_modify_fg(c,GTK_STATE_ACTIVE,rgb);
- gtk_widget_modify_fg(c,GTK_STATE_PRELIGHT,rgb);
- gtk_widget_modify_fg(c,GTK_STATE_SELECTED,rgb);
-
- /* buttons usually have internal labels */
- if(GTK_IS_CONTAINER(c))
- gtk_container_forall (GTK_CONTAINER(c),set_fg,in);
-}
-
-static void set_via_active(struct panel *p, int *active, int *bactive){
- int fi,i;
- int ch=0;
- for(fi=0;fi<inputs;fi++){
- for(i=ch;i<ch+channels[fi];i++)
- gtk_widget_set_sensitive(p->chbuttons[i],1);
- ch+=channels[fi];
- }
- plot_set_active(PLOT(p->plot),active,bactive);
-}
-
-static void chlabels(GtkWidget *widget,struct panel *p){
- /* scan state, update labels on channel buttons, set sensitivity
- based on grouping and mode */
- int fi,ch,i;
- char buf[80];
- int bactive[total_ch];
-
- for(i=0;i<total_ch;i++)
- bactive[i]=active[i]=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p->chbuttons[i]));
-
- /* set sensitivity */
- switch(plot_link){
- case LINK_IMPEDENCE_p1:
- case LINK_IMPEDENCE_1:
- case LINK_IMPEDENCE_10:
- case LINK_THD:
- case LINK_THDN:
- case LINK_THD2:
- case LINK_THDN2:
- case LINK_SUB_REF:
-
- /* first channel in each group insensitive/inactive, used as a reference */
- ch=0;
- for(fi=0;fi<inputs;fi++){
- for(i=ch;i<ch+channels[fi];i++){
- if(i==ch){
- gtk_widget_set_sensitive(p->chbuttons[i],0);
- active[i]=0; /* do not frob widget, only plot settings */
- }else{
- gtk_widget_set_sensitive(p->chbuttons[i],1);
- }
- }
- ch+=channels[fi];
- }
-
- plot_set_active(PLOT(p->plot),active,bactive);
- break;
-
- case LINK_SUMMED: /* summing mode */
- case LINK_SUB_FROM: /* subtract channels from reference */
- ch=0;
- for(fi=0;fi<inputs;fi++){
- int any=0;
- for(i=ch;i<ch+channels[fi];i++){
- if(active[i])any=1;
- active[i]=0;
- }
- active[ch]=any;
- ch+=channels[fi];
- }
-
- set_via_active(p,active,bactive);
- break;
-
- case LINK_INDEPENDENT: /* normal/independent mode */
- set_via_active(p,active,bactive);
- break;
-
- case LINK_PHASE: /* response/phase */
- ch=0;
- for(fi=0;fi<inputs;fi++){
- for(i=ch;i<ch+channels[fi];i++)
- if(channels[fi]<2){
- gtk_widget_set_sensitive(p->chbuttons[i],0);
- active[i]=0;
- }else{
- if(i<ch+2){
- gtk_widget_set_sensitive(p->chbuttons[i],1);
- }else{
- gtk_widget_set_sensitive(p->chbuttons[i],0);
- active[i]=0;
- }
- }
- ch+=channels[fi];
- }
- plot_set_active(PLOT(p->plot),active,bactive);
- break;
- }
-
- /* set labels */
- switch(plot_link){
- case LINK_THD:
- case LINK_THDN:
- case LINK_THD2:
- case LINK_THDN2:
- case LINK_IMPEDENCE_p1:
- case LINK_IMPEDENCE_1:
- case LINK_IMPEDENCE_10:
- case LINK_SUB_REF:
- ch=0;
- for(fi=0;fi<inputs;fi++){
- for(i=ch;i<ch+channels[fi];i++){
- if(i==ch){
- gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"reference");
- }else{
- sprintf(buf,"channel %d", i-ch);
- gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),buf);
- }
- }
- ch+=channels[fi];
- }
- break;
- case LINK_SUB_FROM:
- ch=0;
- for(fi=0;fi<inputs;fi++){
- for(i=ch;i<ch+channels[fi];i++){
- if(i==ch){
- gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"output");
- }else{
- sprintf(buf,"channel %d", i-ch);
- gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),buf);
- }
- }
- ch+=channels[fi];
- }
- break;
-
- case LINK_INDEPENDENT:
- case LINK_SUMMED:
-
- ch=0;
- for(fi=0;fi<inputs;fi++){
- for(i=ch;i<ch+channels[fi];i++){
- sprintf(buf,"channel %d", i-ch);
- gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),buf);
- }
- ch+=channels[fi];
- }
- break;
-
- case LINK_PHASE:
-
- ch=0;
- for(fi=0;fi<inputs;fi++){
- for(i=ch;i<ch+channels[fi];i++){
- if(channels[fi]<2){
- gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"unused");
- }else if(i==ch){
- gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"response");
- }else if(i==ch+1){
- gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"phase");
- }else{
- gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"unused");
- }
- }
- ch+=channels[fi];
- }
- break;
- }
-
- /* set colors */
- switch(plot_link){
- case LINK_SUMMED:
- case LINK_SUB_FROM:
- ch=0;
- for(fi=0;fi<inputs;fi++){
- GdkColor rgb = chcolor(ch);
-
- for(i=ch;i<ch+channels[fi];i++){
- GtkWidget *button=p->chbuttons[i];
- set_fg(button,&rgb);
- }
- ch+=channels[fi];
- }
- break;
-
- default:
- ch=0;
- for(fi=0;fi<inputs;fi++){
- for(i=ch;i<ch+channels[fi];i++){
- GdkColor rgb = chcolor(i);
- GtkWidget *button=p->chbuttons[i];
- set_fg(button,&rgb);
- }
- ch+=channels[fi];
- }
- break;
- }
-
-}
-
-static void reschange(GtkWidget *widget,struct panel *p){
- plot_res=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
- plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
-}
-
-static void scalechange(GtkWidget *widget,struct panel *p){
- plot_scale=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
- plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
-}
-
-static void modechange(GtkWidget *widget,struct panel *p){
- plot_mode=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
- replot(p);
- plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
-}
-
-static void linkchange(GtkWidget *widget,struct panel *p){
- plot_link=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
- replot(p);
- plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
- chlabels(widget,p);
-}
-
-static void runchange(GtkWidget *widget,struct panel *p){
- if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))){
- if(!process_active){
- pthread_t thread_id;
- process_active=1;
- process_exit=0;
- animate_fish(p);
- pthread_create(&thread_id,NULL,&process_thread,NULL);
- }
- }else{
- process_exit=1;
- while(process_active)sched_yield();
- }
-}
-
-static void holdchange(GtkWidget *widget,struct panel *p){
- plot_hold=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
- replot(p);
- plot_draw(PLOT(p->plot));
-}
-
-static void loopchange(GtkWidget *widget,struct panel *p){
- acc_loop=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
-}
-
-static void clearchange(GtkWidget *widget,struct panel *p){
- acc_clear=1;
- plot_clear(PLOT(p->plot));
- if(!process_active){
- rundata_clear();
- }
-}
-
-static void rewindchange(GtkWidget *widget,struct panel *p){
- acc_rewind=1;
-}
-
-extern char *version;
-void panel_create(struct panel *panel){
- int i;
-
- GtkWidget *topplace,*topal,*topalb;
-
- GtkWidget *topframe=gtk_frame_new (NULL);
- GtkWidget *toplabel=gtk_label_new (NULL);
- GtkWidget *quitbutton=gtk_button_new_with_mnemonic("_quit");
- GtkWidget *mainbox=gtk_hbox_new(0,6);
- GdkWindow *root=gdk_get_default_root_window();
- GtkWidget *rightbox=gtk_vbox_new(0,0);
- GtkWidget *leftbox=gtk_vbox_new(0,6);
-
- panel->toplevel=gtk_window_new (GTK_WINDOW_TOPLEVEL);
- panel->group = gtk_accel_group_new ();
- gtk_window_add_accel_group (GTK_WINDOW(panel->toplevel), panel->group);
-
- char versionmarkup[240];
- snprintf(versionmarkup,240," <span size=\"large\" weight=\"bold\" "
- "style=\"italic\" foreground=\"dark blue\">"
- "Spectrum Analyzer</span> <span size=\"small\" foreground=\"#606060\">"
- "revision %s</span> ",
- version);
-
- /* the Fucking Fish */
- for(i=0;i<19;i++)
- panel->ff[i]=gdk_pixmap_create_from_xpm_d(root,
- panel->fb+i,NULL,ff_xpm[i]);
- panel->twirlimage=gtk_image_new_from_pixmap(panel->ff[0],panel->fb[0]);
-
- active = calloc(total_ch,sizeof(*active));
-
- topplace=gtk_table_new(1,1,0);
- topalb=gtk_hbox_new(0,0);
- topal=gtk_alignment_new(1,0,0,0);
-
- gtk_widget_set_name(quitbutton,"quitbutton");
-
- gtk_box_pack_start(GTK_BOX(topalb),quitbutton,0,0,0);
- gtk_container_add (GTK_CONTAINER(topal),topalb);
-
- gtk_table_attach_defaults(GTK_TABLE(topplace),
- topal,0,1,0,1);
- gtk_table_attach_defaults(GTK_TABLE(topplace),
- topframe,0,1,0,1);
-
- gtk_container_add (GTK_CONTAINER (panel->toplevel), topplace);
- gtk_container_set_border_width (GTK_CONTAINER (quitbutton), 3);
-
- g_signal_connect (G_OBJECT (quitbutton), "clicked",
- G_CALLBACK (shutdown), NULL);
- gtk_widget_add_accelerator (quitbutton, "activate", panel->group, GDK_q, 0, 0);
-
- gtk_container_set_border_width (GTK_CONTAINER (topframe), 3);
- gtk_container_set_border_width (GTK_CONTAINER (mainbox), 3);
- gtk_frame_set_shadow_type(GTK_FRAME(topframe),GTK_SHADOW_ETCHED_IN);
- gtk_frame_set_label_widget(GTK_FRAME(topframe),toplabel);
- gtk_label_set_markup(GTK_LABEL(toplabel),versionmarkup);
-
- gtk_container_add (GTK_CONTAINER(topframe), mainbox);
-
- g_signal_connect (G_OBJECT (panel->toplevel), "delete_event",
- G_CALLBACK (shutdown), NULL);
-
- /* add the spectrum plot box */
- panel->plot=plot_new(blocksize/2+1,inputs,channels,rate);
- gtk_box_pack_end(GTK_BOX(leftbox),panel->plot,1,1,0);
- gtk_box_pack_start(GTK_BOX(mainbox),leftbox,1,1,0);
-
- /*fish */
- {
- GtkWidget *box=gtk_hbox_new(1,1);
- GtkWidget *fishbox=gtk_hbox_new(0,0);
- gtk_box_pack_end(GTK_BOX(fishbox),panel->twirlimage,0,0,0);
- gtk_container_set_border_width (GTK_CONTAINER (fishbox), 3);
-
- gtk_box_pack_start(GTK_BOX(box),fishbox,0,0,0);
- gtk_box_pack_start(GTK_BOX(rightbox),box,0,0,0);
- }
-
- /* rate */
- /* channels */
- /* bits */
- {
- int fi;
- int ch=0;
- char buffer[160];
- GtkWidget *label;
- //GtkWidget *vbox=gtk_vbox_new(1,1);
-
- GtkWidget *sep=gtk_hseparator_new();
- gtk_box_pack_start(GTK_BOX(rightbox),sep,0,0,6);
-
- panel->chbuttons = calloc(total_ch,sizeof(*panel->chbuttons));
- for(fi=0;fi<inputs;fi++){
-
- char *lastslash = strrchr(inputname[fi],'/');
- sprintf(buffer,"%s",(lastslash?lastslash+1:inputname[fi]));
- label=gtk_label_new(buffer);
- gtk_widget_set_name(label,"readout");
- gtk_box_pack_start(GTK_BOX(rightbox),label,0,0,0);
-
- sprintf(buffer,"%dHz %dbit",rate[fi],bits[fi]);
- label=gtk_label_new(buffer);
- gtk_widget_set_name(label,"readout");
- gtk_box_pack_start(GTK_BOX(rightbox),label,0,0,0);
-
- for(i=ch;i<ch+channels[fi];i++){
- GtkWidget *button=panel->chbuttons[i]=gtk_toggle_button_new();
-
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),1);
- g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (chlabels), panel);
- gtk_box_pack_start(GTK_BOX(rightbox),button,0,0,0);
- }
-
- GtkWidget *sep=gtk_hseparator_new();
- gtk_box_pack_start(GTK_BOX(rightbox),sep,0,0,6);
-
- ch+=channels[fi];
-
- }
- chlabels(NULL,panel);
- }
-
- GtkWidget *bbox=gtk_vbox_new(0,0);
-
- /* add the action buttons */
- /* scale */
- {
- GtkWidget *menu=gtk_combo_box_new_text();
- char *entries[]={"single-pixel","1/24th octave","1/12th octave","1/3 octave"};
- for(i=0;i<4;i++)
- gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
- gtk_combo_box_set_active(GTK_COMBO_BOX(menu),plot_res);
- gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
- g_signal_connect (G_OBJECT (menu), "changed",
- G_CALLBACK (reschange), panel);
- }
-
- {
- GtkWidget *menu=gtk_combo_box_new_text();
- char *entries[]={"log scale","ISO log scale","linear scale"};
- for(i=0;i<3;i++)
- gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
- gtk_combo_box_set_active(GTK_COMBO_BOX(menu),plot_scale);
- plot_setting(PLOT(panel->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
- gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
-
- g_signal_connect (G_OBJECT (menu), "changed",
- G_CALLBACK (scalechange), panel);
- }
-
- /* depth */
- {
- GtkWidget *menu=gtk_combo_box_new_text();
- char *entries[]={"10dB","20dB","45dB","90dB","140dB"};
- for(i=0;i<5;i++)
- gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
- gtk_combo_box_set_active(GTK_COMBO_BOX(menu),3);
- gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
-
- g_signal_connect (G_OBJECT (menu), "changed",
- G_CALLBACK (depthchange), panel);
- }
-
- /* mode */
- {
- GtkWidget *menu=gtk_combo_box_new_text();
- char *entries[]={"realtime","maximum","accumulate"};
- for(i=0;i<3;i++)
- gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
- gtk_combo_box_set_active(GTK_COMBO_BOX(menu),plot_mode);
- gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
-
- g_signal_connect (G_OBJECT (menu), "changed",
- G_CALLBACK (modechange), panel);
- }
-
- /* link */
- {
- GtkWidget *menu=gtk_combo_box_new_text();
- for(i=0;i<LINKS;i++)
- gtk_combo_box_append_text (GTK_COMBO_BOX (menu), link_entries[i]);
- gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
- gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
-
- g_signal_connect (G_OBJECT (menu), "changed",
- G_CALLBACK (linkchange), panel);
- }
-
- {
- GtkWidget *sep=gtk_hseparator_new();
- gtk_box_pack_start(GTK_BOX(bbox),sep,0,0,4);
- }
-
- /* run/pause */
- {
- GtkWidget *button=gtk_toggle_button_new_with_mnemonic("_run");
- gtk_widget_add_accelerator (button, "activate", panel->group, GDK_space, 0, 0);
- gtk_widget_add_accelerator (button, "activate", panel->group, GDK_r, 0, 0);
- g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (runchange), panel);
- gtk_box_pack_start(GTK_BOX(bbox),button,0,0,0);
- panel->run=button;
- }
-
- /* hold */
- /* loop */
- {
- GtkWidget *box=gtk_hbox_new(1,1);
- GtkWidget *button=gtk_toggle_button_new_with_mnemonic("_hold");
- gtk_widget_add_accelerator (button, "activate", panel->group, GDK_h, 0, 0);
- g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (holdchange), panel);
- gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
-
- button=gtk_toggle_button_new_with_mnemonic("_loop");
- gtk_widget_add_accelerator (button, "activate", panel->group, GDK_l, 0, 0);
- g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (loopchange), panel);
- gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
- gtk_widget_set_sensitive(button,global_seekable);
-
- gtk_box_pack_start(GTK_BOX(bbox),box,0,0,0);
- }
-
- /* clear */
- /* rewind */
- {
- GtkWidget *box=gtk_hbox_new(1,1);
- GtkWidget *button=gtk_button_new_with_mnemonic("_clear");
- gtk_widget_add_accelerator (button, "activate", panel->group, GDK_c, 0, 0);
- g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (clearchange), panel);
- gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
-
- button=gtk_button_new_with_mnemonic("re_wind");
- gtk_widget_add_accelerator (button, "activate", panel->group, GDK_w, 0, 0);
- g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (rewindchange), panel);
- gtk_widget_set_sensitive(button,global_seekable);
- gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
-
- gtk_box_pack_start(GTK_BOX(bbox),box,0,0,0);
- }
-
- /* dump */
- {
- GtkWidget *button=gtk_button_new_with_mnemonic("_dump data");
- gtk_widget_add_accelerator (button, "activate", panel->group, GDK_d, 0, 0);
- g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (dump), panel);
- gtk_box_pack_start(GTK_BOX(bbox),button,0,0,0);
- }
-
- /* noise floor */
- {
- GtkWidget *button=gtk_toggle_button_new_with_mnemonic("sample _noise floor");
- gtk_widget_add_accelerator (button, "activate", panel->group, GDK_n, 0, 0);
- g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (noise), panel);
- gtk_box_pack_start(GTK_BOX(bbox),button,0,0,0);
- }
-
- gtk_box_pack_end(GTK_BOX(rightbox),bbox,0,0,0);
- gtk_box_pack_start(GTK_BOX(mainbox),rightbox,0,0,0);
-
-
- gtk_widget_show_all(panel->toplevel);
- //gtk_window_set_resizable(GTK_WINDOW(panel->toplevel),0);
-
-}
-
-static gboolean async_event_handle(GIOChannel *channel,
- GIOCondition condition,
- gpointer data){
- struct panel *panel=data;
- char buf[1];
-
- /* read all pending */
- while(read(eventpipe[0],buf,1)>0);
-
- increment_fish=1;
-
- /* check playback status and update the run button if needed */
- if(process_active && panel->run &&
- !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel->run)))
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel->run),1);
- if(!process_active && panel->run &&
- gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel->run)))
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel->run),0);
-
- /* update the spectral display; send new data */
- pthread_mutex_lock(&feedback_mutex);
- if(plot_last_update!=feedback_increment){
- pthread_mutex_unlock(&feedback_mutex);
- replot(panel);
- plot_draw(PLOT(panel->plot));
-
- while (gtk_events_pending())
- gtk_main_iteration();
- }else
- pthread_mutex_unlock(&feedback_mutex);
-
- return TRUE;
-}
-
-static int look_for_gtkrc(char *filename){
- FILE *f=fopen(filename,"r");
- if(!f)return 0;
- fprintf(stderr,"Loading spectrum-gtkrc file found at %s\n",filename);
- gtk_rc_add_default_file(filename);
- return 1;
-}
-
-void panel_go(int argc,char *argv[]){
- char *homedir=getenv("HOME");
- int found=0;
- memset(&p,0,sizeof(p));
-
- found|=look_for_gtkrc(ETCDIR"/spectrum-gtkrc");
- {
- char *rcdir=getenv("HOME");
- if(rcdir){
- char *rcfile="/.spectrum/spectrum-gtkrc";
- char *homerc=calloc(1,strlen(rcdir)+strlen(rcfile)+1);
- strcat(homerc,homedir);
- strcat(homerc,rcfile);
- found|=look_for_gtkrc(homerc);
- }
- }
- {
- char *rcdir=getenv("SPECTRUM_RCDIR");
- if(rcdir){
- char *rcfile="/spectrum-gtkrc";
- char *homerc=calloc(1,strlen(rcdir)+strlen(rcfile)+1);
- strcat(homerc,homedir);
- strcat(homerc,rcfile);
- found|=look_for_gtkrc(homerc);
- }
- }
- found|=look_for_gtkrc("./spectrum-gtkrc");
-
- if(!found){
-
- fprintf(stderr,"Could not find the spectrum-gtkrc configuration file normally\n"
- "installed in one of the following places:\n"
-
- "\t./spectrum-gtkrc\n"
- "\t$(SPECTRUM_RCDIR)/spectrum-gtkrc\n"
- "\t~/.spectrum/spectrum-gtkrc\n\t"
- ETCDIR"/spectrum-gtkrc\n"
- "This configuration file is used to tune the color, font and other detail aspects\n"
- "of the user interface. Although the analyzer will work without it, the UI\n"
- "appearence will likely make the application harder to use due to missing visual\n"
- "cues.\n");
- }
-
- gtk_rc_add_default_file(ETCDIR"/spectrum-gtkrc");
- if(homedir){
- char *rcfile="/.spectrum-gtkrc";
- char *homerc=calloc(1,strlen(homedir)+strlen(rcfile)+1);
- strcat(homerc,homedir);
- strcat(homerc,rcfile);
- gtk_rc_add_default_file(homerc);
- }
- gtk_rc_add_default_file(".spectrum-gtkrc");
- gtk_rc_add_default_file("spectrum-gtkrc");
- gtk_init (&argc, &argv);
-
- panel_create(&p);
- animate_fish(&p);
-
- /* set up watching the event pipe */
- {
- GIOChannel *channel = g_io_channel_unix_new (eventpipe[0]);
- guint id;
-
- g_io_channel_set_encoding (channel, NULL, NULL);
- g_io_channel_set_buffered (channel, FALSE);
- g_io_channel_set_close_on_unref (channel, TRUE);
-
- id = g_io_add_watch (channel, G_IO_IN, async_event_handle, &p);
-
- g_io_channel_unref (channel);
-
- }
-
- /* we want to be running by default */
- {
- pthread_t thread_id;
- animate_fish(&p);
- process_active=1;
- pthread_create(&thread_id,NULL,&process_thread,NULL);
- }
-
- gtk_main ();
-
-}
-
Deleted: trunk/spectrum/plot.h
===================================================================
--- trunk/spectrum/plot.h 2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/plot.h 2012-04-12 14:29:17 UTC (rev 18229)
@@ -1,132 +0,0 @@
-/*
- *
- * gtk2 spectrum analyzer
- *
- * Copyright (C) 2004 Monty
- *
- * This analyzer is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * The analyzer is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Postfish; see the file COPYING. If not, write to the
- * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
- */
-
-#ifndef __PLOT_H__
-#define __PLOT_H__
-
-#include <glib.h>
-#include <glib-object.h>
-#include <gtk/gtkcontainer.h>
-#include <gtk/gtksignal.h>
-#include <gtk/gtkdrawingarea.h>
-
-G_BEGIN_DECLS
-
-#define PLOT_TYPE (plot_get_type ())
-#define PLOT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PLOT_TYPE, Plot))
-#define PLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PLOT_TYPE, PlotClass))
-#define IS_PLOT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PLOT_TYPE))
-#define IS_PLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PLOT_TYPE))
-
-typedef struct _Plot Plot;
-typedef struct _PlotClass PlotClass;
-
-struct _Plot{
-
- GtkDrawingArea canvas;
- GdkPixmap *backing;
- GdkGC *drawgc;
- GdkGC *dashes;
-
- PangoLayout **lin_layout;
- PangoLayout **log_layout;
- PangoLayout **iso_layout;
- PangoLayout **db_layout;
- PangoLayout **imp_layout;
- PangoLayout **phase_layout;
-
- int configured;
- float **ydata;
- float *floor;
-
- int groups;
- int *ch;
- int *ch_active;
- int *ch_process;
- int total_ch;
- int datasize;
- int mode;
- int link;
- int scale;
- int res;
- int noise;
- int *rate;
- int maxrate;
-
- float lin_major;
- float lin_minor;
- int lin_mult;
-
- int xgrid[20];
- int xgrids;
- int xtic[200];
- int xtics;
-
- int ygrid[11];
- int ygrids;
- int ytic[200];
- int ytics;
-
- float depth;
- float ymax;
- float pmax;
- float pmin;
-
- float disp_depth;
- float disp_ymax;
- float disp_pmax;
- float disp_pmin;
-
- float padx;
- float phax;
- float pady;
-
- int ymaxtimer;
- int phtimer;
-};
-
-struct _PlotClass{
-
- GtkDrawingAreaClass parent_class;
- void (* plot) (Plot *m);
-};
-
-GType plot_get_type (void);
-GtkWidget* plot_new (int n, int inputs, int *channels, int *rate);
-void plot_refresh (Plot *m, int *process);
-void plot_setting (Plot *m, int res, int scale, int mode, int link, int depth, int noise);
-void plot_draw (Plot *m);
-void plot_clear (Plot *m);
-int plot_width (Plot *m);
-float** plot_get (Plot *m);
-void plot_set_active (Plot *m, int *, int *);
-
-GdkColor chcolor(int ch);
-
-G_END_DECLS
-
-#endif
-
-
-
-
Deleted: trunk/spectrum/process.c
===================================================================
--- trunk/spectrum/process.c 2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/process.c 2012-04-12 14:29:17 UTC (rev 18229)
@@ -1,923 +0,0 @@
-/*
- *
- * gtk2 spectrum analyzer
- *
- * Copyright (C) 2004 Monty
- *
- * This analyzer is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * The analyzer is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Postfish; see the file COPYING. If not, write to the
- * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *
- */
-
-#include "analyzer.h"
-#include "io.h"
-
-static float *window=NULL;
-static float *freqbuffer=0;
-static fftwf_plan plan;
-
-pthread_mutex_t feedback_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
-int feedback_increment=0;
-
-float *feedback_count;
-float **plot_data;
-float *plot_floor=NULL;
-float **work_floor=NULL;
-float *process_work;
-
-float **feedback_acc;
-float **feedback_max;
-float **feedback_instant;
-
-/* Gentlemen, power up the Variance hammer */
-float **floor_y;
-float **floor_yy;
-int floor_count;
-
-float **ph_acc;
-float **ph_max;
-float **ph_instant;
-
-float **xmappingL;
-float **xmappingH;
-int metascale = -1;
-int metawidth = -1;
-int metares = -1;
-int metanoise = 0;
-
-sig_atomic_t acc_clear=0;
-sig_atomic_t acc_rewind=0;
-sig_atomic_t acc_loop=0;
-
-sig_atomic_t process_active=0;
-sig_atomic_t process_exit=0;
-
-static void init_process(void){
- int i;
- if(window==NULL){
- process_work=calloc(blocksize+2,sizeof(*process_work));
- feedback_count=calloc(total_ch,sizeof(*feedback_count));
- plot_data=calloc(total_ch,sizeof(*plot_data));
-
- feedback_acc=malloc(total_ch*sizeof(*feedback_acc));
- feedback_max=malloc(total_ch*sizeof(*feedback_max));
- feedback_instant=malloc(total_ch*sizeof(*feedback_instant));
- floor_y=malloc(total_ch*sizeof(*floor_y));
- floor_yy=malloc(total_ch*sizeof(*floor_yy));
-
- ph_acc=malloc(total_ch*sizeof(*ph_acc));
- ph_max=malloc(total_ch*sizeof(*ph_max));
- ph_instant=malloc(total_ch*sizeof(*ph_instant));
-
- freqbuffer=fftwf_malloc((blocksize+2)*sizeof(*freqbuffer));
- for(i=0;i<total_ch;i++){
-
- floor_y[i]=calloc(blocksize/2+1,sizeof(**floor_y));
- floor_yy[i]=calloc(blocksize/2+1,sizeof(**floor_yy));
- feedback_acc[i]=calloc(blocksize/2+1,sizeof(**feedback_acc));
- feedback_max[i]=calloc(blocksize/2+1,sizeof(**feedback_max));
- feedback_instant[i]=calloc(blocksize/2+1,sizeof(**feedback_instant));
-
- ph_acc[i]=calloc(blocksize+2,sizeof(**ph_acc));
- ph_max[i]=calloc(blocksize+2,sizeof(**ph_max));
- ph_instant[i]=calloc(blocksize+2,sizeof(**ph_instant));
- }
-
- plan=fftwf_plan_dft_r2c_1d(blocksize,freqbuffer,
- (fftwf_complex *)freqbuffer,
- FFTW_ESTIMATE);
-
- /* construct proper window (sin^4 I'd think) */
- window = calloc(blocksize,sizeof(*window));
- for(i=0;i<blocksize;i++)window[i]=sin(M_PIl*i/blocksize);
- for(i=0;i<blocksize;i++)window[i]*=window[i];
- for(i=0;i<blocksize;i++)window[i]=sin(window[i]*M_PIl*.5);
- for(i=0;i<blocksize;i++)window[i]*=window[i]/(blocksize/4)*.778;
- }
-}
-
-void rundata_clear(){
- int i,j;
- for(i=0;i<total_ch;i++){
- feedback_count[i]=0;
- memset(feedback_acc[i],0,(blocksize/2+1)*sizeof(**feedback_acc));
- memset(feedback_max[i],0,(blocksize/2+1)*sizeof(**feedback_max));
- memset(feedback_instant[i],0,(blocksize/2+1)*sizeof(**feedback_instant));
-
- for(j=0;j<blocksize+2;j++){
- ph_acc[i][j]=0;
- ph_max[i][j]=0;
- ph_instant[i][j]=0;
- }
- }
- acc_clear=0;
-}
-
-extern int plot_noise;
-
-/* return 0 on EOF, 1 otherwise */
-static int process(){
- int fi,i,j,ch;
- int eof_all;
- int noise=plot_noise;
-
- if(acc_rewind)
- rewind_files();
-
- eof_all=input_read();
-
- if(eof_all){
- if(acc_loop && !acc_rewind){
- acc_rewind=1;
- return process();
- } else {
- acc_rewind=0;
- return 0;
- }
- }
- acc_rewind=0;
-
- if(acc_clear)
- rundata_clear();
-
- /* by channel */
- ch=0;
- for(fi=0;fi<inputs;fi++){
- if(blockbufferfill[fi]){
- for(i=ch;i<ch+channels[fi];i++){
-
- float *data=blockbuffer[i];
-
- /* window the blockbuffer into the FFT buffer */
- for(j=0;j<blocksize;j++){
- freqbuffer[j]=data[j]*window[j];
- }
-
- /* transform */
- fftwf_execute(plan);
-
- pthread_mutex_lock(&feedback_mutex);
-
- /* perform desired accumulations */
- for(j=0;j<blocksize+2;j+=2){
- float R = freqbuffer[j];
- float I = freqbuffer[j+1];
- float sqR = R*R;
- float sqI = I*I;
- float sqM = sqR+sqI;
-
- if(noise==1){
- floor_yy[i][j>>1]+=sqM*sqM;
- floor_y[i][j>>1]+=sqM;
- }
-
- /* deal with phase accumulate/rotate */
- if(i==ch){
- /* normalize/store ref for later rotation */
- process_work[j] = R;
- process_work[j+1] = -I;
-
- }else{
- /* rotate signed square phase according to ref for phase calculation */
- float pR;
- float pI;
- float rR = process_work[j];
- float rI = process_work[j+1];
- pR = (rR*R - rI*I);
- pI = (rR*I + rI*R);
-
- ph_instant[i][j]=pR;
- ph_instant[i][j+1]=pI;
-
- ph_acc[i][j]+=pR;
- ph_acc[i][j+1]+=pI;
-
- if(feedback_max[i][j>>1]<sqM){
- ph_max[i][j]=pR;
- ph_max[i][j+1]=pI;
- }
- }
-
- feedback_instant[i][j>>1]=sqM;
- feedback_acc[i][j>>1]+=sqM;
-
- if(feedback_max[i][j>>1]<sqM)
- feedback_max[i][j>>1]=sqM;
-
- }
- feedback_count[i]++;
-
- pthread_mutex_unlock(&feedback_mutex);
- }
- }
- ch+=channels[fi];
- }
- if(noise==1)
- floor_count++;
- feedback_increment++;
- write(eventpipe[1],"",1);
- return 1;
-}
-
-void *process_thread(void *dummy){
- while(!process_exit && process());
- process_active=0;
- write(eventpipe[1],"",1);
- return NULL;
-}
-
-void process_dump(int mode){
- int fi,i,j,ch;
- FILE *out;
-
- {
- out=fopen("accumulate.m","w");
- ch = 0;
- for(fi=0;fi<inputs;fi++){
- for(i=0;i<blocksize/2+1;i++){
- fprintf(out,"%f ",(double)i*rate[fi]/blocksize);
-
- for(j=ch;j<ch+channels[fi];j++)
- fprintf(out,"%f ",todB(feedback_acc[j][i])*.5);
- fprintf(out,"\n");
- }
- fprintf(out,"\n");
- ch+=channels[fi];
- }
- fclose(out);
- }
-
- {
- out=fopen("max.m","w");
- ch = 0;
- for(fi=0;fi<inputs;fi++){
- for(i=0;i<blocksize/2+1;i++){
- fprintf(out,"%f ",(double)i*rate[fi]/blocksize);
-
- for(j=ch;j<ch+channels[fi];j++)
- fprintf(out,"%f ",todB(feedback_max[j][i])*.5);
- fprintf(out,"\n");
- }
- fprintf(out,"\n");
- ch+=channels[fi];
- }
- fclose(out);
- }
-
- {
- out=fopen("instant.m","w");
- ch = 0;
- for(fi=0;fi<inputs;fi++){
- for(i=0;i<blocksize/2+1;i++){
- fprintf(out,"%f ",(double)i*rate[fi]/blocksize);
-
- for(j=ch;j<ch+channels[fi];j++)
- fprintf(out,"%f ",todB(feedback_instant[j][i])*.5);
- fprintf(out,"\n");
- }
- fprintf(out,"\n");
- ch+=channels[fi];
- }
- fclose(out);
- }
-
- {
- out=fopen("accphase.m","w");
- ch = 0;
- for(fi=0;fi<inputs;fi++){
-
- /* phase */
- for(i=0;i<blocksize+2;i+=2){
- fprintf(out,"%f ",(double)i*.5*rate[fi]/blocksize);
- fprintf(out,"%f ",atan2(ph_acc[ch+1][i+1],ph_acc[ch+1][i])*57.29);
- fprintf(out,"\n");
- }
- fprintf(out,"\n");
- ch+=channels[fi];
- }
- fclose(out);
- }
-
-}
-
-void clear_noise_floor(){
- int i;
- for(i=0;i<total_ch;i++){
- memset(floor_y[i],0,(blocksize/2+1)*sizeof(**floor_y));
- memset(floor_yy[i],0,(blocksize/2+1)*sizeof(**floor_yy));
- }
- floor_count=0;
-}
-
-/* how many bins to 'trim' off the edge of calculated data when we
- know we've hit a boundary of marginal measurement */
-#define binspan 5
-
-float **process_fetch(int res, int scale, int mode, int link,
- int *active, int width,
- float *ymax, float *pmax, float *pmin,
- float **yfloor,int noise){
- int ch,ci,i,j,fi;
- float **data;
- float **ph;
- float maxrate=-1.;
- float nyq;
-
- init_process();
-
- for(fi=0;fi<inputs;fi++)
- if(rate[fi]>maxrate)maxrate=rate[fi];
- nyq=maxrate/2.;
-
- *yfloor=NULL;
-
- /* are our scale mappings up to date? */
- if(res != metares || scale != metascale || width != metawidth){
- if(!xmappingL) xmappingL = calloc(inputs, sizeof(*xmappingL));
- if(!xmappingH) xmappingH = calloc(inputs, sizeof(*xmappingH));
- metanoise=-1;
-
- if(!work_floor)
- work_floor = calloc(total_ch,sizeof(*work_floor));
- for(i=0;i<total_ch;i++){
- if(work_floor[i])
- work_floor[i] = realloc(work_floor[i],(width+1)*sizeof(**work_floor));
- else
- work_floor[i] = calloc((width+1),sizeof(**work_floor));
- }
-
- for(fi=0;fi<inputs;fi++){
-
- /* if mapping preexists, resize it */
- if(xmappingL[fi]){
- xmappingL[fi] = realloc(xmappingL[fi],(width+1)*sizeof(**xmappingL));
- }else{
- xmappingL[fi] = malloc((width+1)*sizeof(**xmappingL));
- }
- if(xmappingH[fi]){
- xmappingH[fi] = realloc(xmappingH[fi],(width+1)*sizeof(**xmappingH));
- }else{
- xmappingH[fi] = malloc((width+1)*sizeof(**xmappingH));
- }
-
- metascale = scale;
- metawidth = width;
- metares = res;
-
-
- /* generate new numbers */
- for(i=0;i<width;i++){
- float off=0;
- float loff=1.;
- float hoff=1.;
- float lfreq,hfreq;
-
- switch(res){
- case 0: /* screen-resolution */
- off=1.;
- break;
- case 1: /* 1/24th octave */
- loff = .95918945710913818816;
- hoff = 1.04254690518999138632;
- break;
- case 2: /* 1/12th octave */
- loff = .94387431268169349664;
- hoff = 1.05946309435929526455;
- break;
- case 3: /* 1/3th octave */
- loff = .79370052598409973738;
- hoff = 1.25992104989487316475;
- break;
- }
-
- switch(scale){
- case 0: /* log */
- lfreq= pow(10.,(i-off)/(width-1)
- * (log10(nyq)-log10(5.))
- + log10(5.)) * loff;
- hfreq= pow(10.,(i+off)/(width-1)
- * (log10(nyq)-log10(5.))
- + log10(5.)) * hoff;
- break;
- case 1: /* ISO */
- lfreq= pow(2.,(i-off)/(width-1)
- * (log2(nyq)-log2(25.))
- + log2(25.)) * loff;
- hfreq= pow(2.,(i+off)/(width-1)
- * (log2(nyq)-log2(25.))
- + log2(25.)) *hoff;
- break;
- case 2: /* screen-resolution linear */
- lfreq=(i-off)*nyq/(width-1)*loff;
- hfreq=(i+off)*nyq/(width-1)*hoff;
- break;
- }
-
- xmappingL[fi][i]=lfreq/(rate[fi]*.5)*(blocksize/2);
- xmappingH[fi][i]=hfreq/(rate[fi]*.5)*(blocksize/2);
-
- }
-
- for(i=0;i<width;i++){
- if(xmappingL[fi][i]<0.)xmappingL[fi][i]=0.;
- if(xmappingL[fi][i]>blocksize/2.)xmappingL[fi][i]=blocksize/2.;
- if(xmappingH[fi][i]<0.)xmappingH[fi][i]=0.;
- if(xmappingH[fi][i]>blocksize/2.)xmappingH[fi][i]=blocksize/2.;
- }
- }
-
- for(i=0;i<total_ch;i++)
- if(plot_data[i]){
- plot_data[i] = realloc(plot_data[i],(width+1)*sizeof(**plot_data));
- }else{
- plot_data[i] = malloc((width+1)*sizeof(**plot_data));
- }
- }
-
- /* 'illustrate' the noise floor */
- if(noise){
- if(plot_floor)
- plot_floor=realloc(plot_floor,(width+1)*sizeof(*plot_floor));
- else
- plot_floor=calloc((width+1),sizeof(*plot_floor));
-
- if(metanoise!=link){
- float *y = plot_floor;
- int ch=0;
- metanoise=link;
- for(i=0;i<width;i++)
- y[i]=-300;
-
- for(fi=0;fi<inputs;fi++){
- float *L = xmappingL[fi];
- float *H = xmappingH[fi];
- float d = 1./floor_count;
-
- for(ci=0;ci<channels[fi];ci++){
- float *fy = floor_y[ci+ch];
- float *fyy = floor_yy[ci+ch];
- float *w = work_floor[ci+ch];
-
- for(i=0;i<width;i++){
- int first=floor(L[i]);
- int last=floor(H[i]);
- float esum;
- float vsum;
- float v = fyy[first]*floor_count - fy[first]*fy[first];
-
- if(first==last){
- float del=H[i]-L[i];
- esum=fy[first]*del;
- vsum=v*del;
- }else{
- float del=1.-(L[i]-first);
- esum=fy[first]*del;
- vsum=v*del;
-
- for(j=first+1;j<last;j++){
- v = fyy[j]*floor_count - fy[j]*fy[j];
- esum+=fy[j];
- vsum+=v;
- }
-
- v = fyy[last]*floor_count - fy[last]*fy[last];
- del=(H[i]-last);
- esum+=fy[last]*del;
- vsum+=v*del;
- }
- vsum = 10*sqrt(vsum)*d;
- esum*=d;
- w[i] = esum+vsum*10;
- esum = todB_a(w+i)*.5;
-
- if(esum>y[i])y[i]=esum;
- }
- }
- ch+=channels[fi];
- }
- }
- if(link == LINK_INDEPENDENT && mode==0)
- *yfloor=plot_floor;
- }else{
- for(i=0;i<total_ch;i++)
- memset(work_floor[i],0,width*sizeof(**work_floor));
- metanoise=-1;
- }
-
- /* mode selects the base data set */
- switch(mode){
- case 0: /* independent / instant */
- data=feedback_instant;
- ph=ph_instant;
- break;
- case 1: /* independent / max */
- data=feedback_max;
- ph=ph_max;
- break;
- case 2:
- data=feedback_acc;
- ph=ph_acc;
- break;
- }
-
- ch=0;
- *ymax = -150.;
- *pmax = -180.;
- *pmin = 180.;
- for(fi=0;fi<inputs;fi++){
- float *L = xmappingL[fi];
- float *H = xmappingH[fi];
-
- switch(link){
- case LINK_INDEPENDENT:
-
- for(ci=0;ci<channels[fi];ci++){
- float *y = plot_data[ci+ch];
- float *m = data[ci+ch];
- if(active[ch+ci]){
- for(i=0;i<width;i++){
- int first=floor(L[i]);
- int last=floor(H[i]);
- float sum;
-
- if(first==last){
- float del=H[i]-L[i];
- sum=m[first]*del;
- }else{
- float del=1.-(L[i]-first);
- sum=m[first]*del;
-
- for(j=first+1;j<last;j++)
- sum+=m[j];
-
- del=(H[i]-last);
- sum+=m[last]*del;
- }
-
- sum=todB_a(&sum)*.5;
- if(sum>*ymax)*ymax=sum;
- y[i]=sum;
- }
- }
- }
- break;
-
- case LINK_SUMMED:
- {
- float *y = plot_data[ch];
- memset(y,0,(width+1)*sizeof(*y));
-
- for(ci=0;ci<channels[fi];ci++){
- float *m = data[ci+ch];
- if(active[ch+ci]){
- for(i=0;i<width;i++){
- int first=floor(L[i]);
- int last=floor(H[i]);
-
- if(first==last){
- float del=H[i]-L[i];
- y[i]+=m[first]*del;
- }else{
- float del=1.-(L[i]-first);
- y[i]+=m[first]*del;
-
- for(j=first+1;j<last;j++)
- y[i]+=m[j];
-
- del=(H[i]-last);
- y[i]+=m[last]*del;
- }
- }
- }
- }
-
- for(i=0;i<width;i++){
- float sum=todB_a(y+i)*.5;
- if(sum>*ymax)*ymax=sum;
- y[i]=sum;
- }
- }
- break;
-
- case LINK_SUB_FROM:
- {
- float *y = plot_data[ch];
- if(active[ch]==0){
- for(i=0;i<width;i++)
- y[i]=-300;
- }else{
- for(ci=0;ci<channels[fi];ci++){
- float *m = data[ci+ch];
- if(ci==0 || active[ch+ci]){
- for(i=0;i<width;i++){
- int first=floor(L[i]);
- int last=floor(H[i]);
- float sum;
-
- if(first==last){
- float del=H[i]-L[i];
- sum=m[first]*del;
- }else{
- float del=1.-(L[i]-first);
- sum=m[first]*del;
-
- for(j=first+1;j<last;j++)
- sum+=m[j];
-
- del=(H[i]-last);
- sum+=m[last]*del;
- }
-
- if(ci==0){
- y[i]=sum;
- }else{
- y[i]-=sum;
- }
- }
- }
- }
-
- for(i=0;i<width;i++){
- float v = (y[i]>0?y[i]:0);
- float sum=todB_a(&v)*.5;
- if(sum>*ymax)*ymax=sum;
- y[i]=sum;
- }
- }
- }
- break;
- case LINK_SUB_REF:
- {
- float *r = plot_data[ch];
- for(ci=0;ci<channels[fi];ci++){
- float *y = plot_data[ch+ci];
- float *m = data[ci+ch];
- if(ci==0 || active[ch+ci]){
- for(i=0;i<width;i++){
- int first=floor(L[i]);
- int last=floor(H[i]);
- float sum;
-
- if(first==last){
- float del=H[i]-L[i];
- sum=m[first]*del;
- }else{
- float del=1.-(L[i]-first);
- sum=m[first]*del;
-
- for(j=first+1;j<last;j++)
- sum+=m[j];
-
- del=(H[i]-last);
- sum+=m[last]*del;
- }
-
- if(ci==0){
- r[i]=sum;
- }else{
- sum=(r[i]>sum?0.f:sum-r[i]);
- y[i]=todB_a(&sum)*.5;
- if(y[i]>*ymax)*ymax=y[i];
- }
- }
- }
- }
- }
- break;
-
- case LINK_IMPEDENCE_p1:
- case LINK_IMPEDENCE_1:
- case LINK_IMPEDENCE_10:
- {
- float shunt = (link == LINK_IMPEDENCE_p1?.1:(link == LINK_IMPEDENCE_1?1:10));
- float *r = plot_data[ch];
-
- for(ci=0;ci<channels[fi];ci++){
- float *y = plot_data[ci+ch];
- float *m = data[ch+ci];
-
- if(ci==0 || active[ch+ci]){
- for(i=0;i<width;i++){
- int first=floor(L[i]);
- int last=floor(H[i]);
- float sum;
-
- if(first==last){
- float del=H[i]-L[i];
- sum=m[first]*del;
- }else{
- float del=1.-(L[i]-first);
- sum=m[first]*del;
-
- for(j=first+1;j<last;j++)
- sum+=m[j];
-
- del=(H[i]-last);
- sum+=m[last]*del;
- }
-
- if(ci==0){
- /* stash the reference in the work vector */
- r[i]=sum;
- }else{
- /* the shunt */
- /* 'r' collected at source, 'sum' across the shunt */
- float V=sqrt(r[i]);
- float S=sqrt(sum);
-
- if(S>(1e-5) && V>S){
- y[i] = shunt*(V-S)/S;
- }else{
- y[i] = NAN;
- }
- }
- }
- }
- }
-
- /* scan the resulting buffers for marginal data that would
- produce spurious output. Specifically we look for sharp
- falloffs of > 40dB or an original test magnitude under
- -70dB. */
- {
- float max = -140;
- for(i=0;i<width;i++){
- float v = r[i] = todB_a(r+i)*.5;
- if(v>max)max=v;
- }
-
- for(ci=1;ci<channels[fi];ci++){
- if(active[ch+ci]){
- float *y = plot_data[ci+ch];
- for(i=0;i<width;i++){
- if(r[i]<max-40 || r[i]<-70){
- int j=i-binspan;
- if(j<0)j=0;
- for(;j<i;j++)
- y[j]=NAN;
- for(;j<width;j++){
- if(r[j]>max-40 && r[j]>-70)break;
- y[j]=NAN;
- }
- i=j+3;
- for(;j<i && j<width;j++){
- y[j]=NAN;
- }
- }
- if(!isnan(y[i]) && y[i]>*ymax)*ymax = y[i];
- }
- }
- }
- }
- }
- break;
-
- case LINK_PHASE: /* response/phase */
-
- if(channels[fi]>=2){
- float *om = plot_data[ch];
- float *op = plot_data[ch+1];
-
- float *r = data[ch];
- float *rn = work_floor[ch];
- float *m = data[ch+1];
- float *mn = work_floor[ch+1];
- float *p = ph[ch+1];
- float mag[width];
-
- if(feedback_count[ch]==0){
- memset(om,0,width*sizeof(*om));
- memset(op,0,width*sizeof(*op));
- }else{
- /* two vectors only; response and phase */
- /* response */
- if(active[ch] || active[ch+1]){
- for(i=0;i<width;i++){
- int first=floor(L[i]);
- int last=floor(H[i]);
- float sumR,sumM;
-
- if(first==last){
- float del=H[i]-L[i];
- sumR=r[first]*del;
- sumM=m[first]*del;
- }else{
- float del=1.-(L[i]-first);
- sumR=r[first]*del;
- sumM=m[first]*del;
-
- for(j=first+1;j<last;j++){
- sumR+=r[j];
- sumM+=m[j];
- }
-
- del=(H[i]-last);
- sumR+=r[last]*del;
- sumM+=m[last]*del;
- }
-
- if(sumR>rn[i] && sumM>mn[i]){
- mag[i] = todB_a(&sumR)*.5;
- sumM /= sumR;
- om[i] = todB_a(&sumM)*.5;
- }else{
- om[i] = NAN;
- }
- }
- }
-
- /* phase */
- if(active[ch+1]){
- for(i=0;i<width;i++){
- int first=floor(L[i]);
- int last=floor(H[i]);
- float sumR,sumI;
-
- if(first==last){
- float del=H[i]-L[i];
- sumR=p[(first<<1)]*del;
- sumI=p[(first<<1)+1]*del;
- }else{
- float del=1.-(L[i]-first);
- sumR=p[(first<<1)]*del;
- sumI=p[(first<<1)+1]*del;
-
- for(j=first+1;j<last;j++){
- sumR+=p[(j<<1)];
- sumI+=p[(j<<1)+1];
- }
-
- del=(H[i]-last);
- sumR+=p[(last<<1)]*del;
- sumI+=p[(last<<1)+1]*del;
- }
-
- if(!isnan(om[i])){
- op[i] = atan2(sumI,sumR)*57.29;
- }else{
- op[i]=NAN;
- }
- }
- }
-
- /* scan the resulting buffers for marginal data that would
- produce spurious output. Specifically we look for sharp
- falloffs of > 40dB or an original test magnitude under
- -70dB. */
- if(active[ch] || active[ch+1]){
- for(i=0;i<width;i++){
- if(isnan(om[i])){
- int j=i-binspan;
- if(j<0)j=0;
- for(;j<i;j++){
- om[j]=NAN;
- op[j]=NAN;
- }
- for(;j<width;j++){
- if(!isnan(om[j]))break;
- om[j]=NAN;
- op[j]=NAN;
- }
- i=j+3;
- for(;j<i && j<width;j++){
- om[j]=NAN;
- op[j]=NAN;
- }
- }
- if(om[i]>*ymax)*ymax = om[i];
- if(op[i]>*pmax)*pmax = op[i];
- if(op[i]<*pmin)*pmin = op[i];
-
- }
- }
- }
- }
- break;
-
- case LINK_THD: /* THD */
- case LINK_THD2: /* THD-2 */
- case LINK_THDN: /* THD+N */
- case LINK_THDN2: /* THD+N-2 */
-
-
- break;
-
- }
- ch+=channels[fi];
- }
-
- return plot_data;
-}
-
Copied: trunk/spectrum/spec_panel.c (from rev 14614, trunk/spectrum/panel.c)
===================================================================
--- trunk/spectrum/spec_panel.c (rev 0)
+++ trunk/spectrum/spec_panel.c 2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,841 @@
+/*
+ *
+ * gtk2 spectrum analyzer
+ *
+ * Copyright (C) 2004 Monty
+ *
+ * This analyzer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The analyzer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Postfish; see the file COPYING. If not, write to the
+ * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#include "analyzer.h"
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "fisharray.h"
+#include "spec_plot.h"
+#include "io.h"
+
+sig_atomic_t increment_fish=0;
+
+static struct panel {
+ GtkWidget *twirlimage;
+ GdkPixmap *ff[19];
+ GdkBitmap *fb[19];
+
+ GtkAccelGroup *group;
+ GtkWidget *toplevel;
+
+
+ guint fishframe_timer;
+ int fishframe_init;
+ int fishframe;
+
+ GtkWidget *plot;
+ GtkWidget *run;
+ GtkWidget **chbuttons;
+
+} p;
+
+int plot_res=0;
+int plot_scale=0;
+int plot_mode=0;
+int plot_link=0;
+int plot_hold=0;
+int plot_depth=90;
+int plot_noise=0;
+int plot_last_update=0;
+int *active;
+
+static void replot(struct panel *p){
+ int i,lactive[total_ch];
+ for(i=0;i<total_ch;i++)
+ lactive[i]=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p->chbuttons[i]));
+
+ /* update the spectral display; send new data */
+ if(!plot_hold){
+ pthread_mutex_lock(&feedback_mutex);
+ plot_refresh(PLOT(p->plot),lactive);
+ plot_last_update=feedback_increment;
+ pthread_mutex_unlock(&feedback_mutex);
+ }
+}
+
+static void shutdown(void){
+ gtk_main_quit();
+}
+
+/* gotta have the Fucking Fish */
+static int reanimate_fish(struct panel *p){
+ if(process_active || (p->fishframe>0 && p->fishframe<12)){
+ /* continue spinning */
+ if(increment_fish)p->fishframe++;
+ if(p->fishframe>=12)p->fishframe=0;
+
+ gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
+ p->ff[p->fishframe],
+ p->fb[p->fishframe]);
+
+ if(p->fishframe==0 && !process_active){
+ /* reschedule to blink */
+ p->fishframe_timer=
+ g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+ return FALSE;
+ }
+
+ }else{
+ p->fishframe++;
+ if(p->fishframe<=1)p->fishframe=12;
+ if(p->fishframe>=19)p->fishframe=0;
+
+ gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
+ p->ff[p->fishframe],
+ p->fb[p->fishframe]);
+
+
+ if(p->fishframe==12){
+ /* reschedule to animate */
+ p->fishframe_timer=
+ g_timeout_add(10,(GSourceFunc)reanimate_fish,p);
+ return FALSE;
+ }
+ if(p->fishframe==0){
+ /* reschedule to blink */
+ p->fishframe_timer=
+ g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static void animate_fish(struct panel *p){
+ if(p->fishframe_init){
+ g_source_remove(p->fishframe_timer);
+ p->fishframe_timer=
+ g_timeout_add(80,(GSourceFunc)reanimate_fish,p);
+ }else{
+ p->fishframe_init=1;
+ p->fishframe_timer=
+ g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+ }
+}
+
+static void dump(GtkWidget *widget,struct panel *p){
+ process_dump(plot_mode);
+}
+
+static void noise(GtkWidget *widget,struct panel *p){
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))){
+ if(plot_noise){
+ plot_noise=0;
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget),0);
+ clear_noise_floor();
+ plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,0);
+ }else{
+ plot_noise=1;
+ plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,0);
+ }
+ }else{
+ if(plot_noise){
+ gtk_button_set_label(GTK_BUTTON(widget),"clear _noise floor");
+ plot_noise=2;
+ plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,1);
+ }else
+ gtk_button_set_label(GTK_BUTTON(widget),"sample _noise floor");
+ }
+}
+
+
+static void depthchange(GtkWidget *widget,struct panel *p){
+ int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+ switch(choice){
+ case 0: /* 10dB */
+ plot_depth=10;
+ break;
+ case 1: /* 20dB */
+ plot_depth=20;
+ break;
+ case 2: /* 45dB */
+ plot_depth=45;
+ break;
+ case 3: /* 90dB */
+ plot_depth=90;
+ break;
+ case 4: /*140dB */
+ plot_depth=140;
+ break;
+ }
+ plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
+}
+
+static void set_fg(GtkWidget *c, gpointer in){
+ GdkColor *rgb = in;
+ gtk_widget_modify_fg(c,GTK_STATE_NORMAL,rgb);
+ gtk_widget_modify_fg(c,GTK_STATE_ACTIVE,rgb);
+ gtk_widget_modify_fg(c,GTK_STATE_PRELIGHT,rgb);
+ gtk_widget_modify_fg(c,GTK_STATE_SELECTED,rgb);
+
+ /* buttons usually have internal labels */
+ if(GTK_IS_CONTAINER(c))
+ gtk_container_forall (GTK_CONTAINER(c),set_fg,in);
+}
+
+static void set_via_active(struct panel *p, int *active, int *bactive){
+ int fi,i;
+ int ch=0;
+ for(fi=0;fi<inputs;fi++){
+ for(i=ch;i<ch+channels[fi];i++)
+ gtk_widget_set_sensitive(p->chbuttons[i],1);
+ ch+=channels[fi];
+ }
+ plot_set_active(PLOT(p->plot),active,bactive);
+}
+
+static void chlabels(GtkWidget *widget,struct panel *p){
+ /* scan state, update labels on channel buttons, set sensitivity
+ based on grouping and mode */
+ int fi,ch,i;
+ char buf[80];
+ int bactive[total_ch];
+
+ for(i=0;i<total_ch;i++)
+ bactive[i]=active[i]=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p->chbuttons[i]));
+
+ /* set sensitivity */
+ switch(plot_link){
+ case LINK_IMPEDENCE_p1:
+ case LINK_IMPEDENCE_1:
+ case LINK_IMPEDENCE_10:
+ case LINK_THD:
+ case LINK_THDN:
+ case LINK_THD2:
+ case LINK_THDN2:
+ case LINK_SUB_REF:
+
+ /* first channel in each group insensitive/inactive, used as a reference */
+ ch=0;
+ for(fi=0;fi<inputs;fi++){
+ for(i=ch;i<ch+channels[fi];i++){
+ if(i==ch){
+ gtk_widget_set_sensitive(p->chbuttons[i],0);
+ active[i]=0; /* do not frob widget, only plot settings */
+ }else{
+ gtk_widget_set_sensitive(p->chbuttons[i],1);
+ }
+ }
+ ch+=channels[fi];
+ }
+
+ plot_set_active(PLOT(p->plot),active,bactive);
+ break;
+
+ case LINK_SUMMED: /* summing mode */
+ case LINK_SUB_FROM: /* subtract channels from reference */
+ ch=0;
+ for(fi=0;fi<inputs;fi++){
+ int any=0;
+ for(i=ch;i<ch+channels[fi];i++){
+ if(active[i])any=1;
+ active[i]=0;
+ }
+ active[ch]=any;
+ ch+=channels[fi];
+ }
+
+ set_via_active(p,active,bactive);
+ break;
+
+ case LINK_INDEPENDENT: /* normal/independent mode */
+ set_via_active(p,active,bactive);
+ break;
+
+ case LINK_PHASE: /* response/phase */
+ ch=0;
+ for(fi=0;fi<inputs;fi++){
+ for(i=ch;i<ch+channels[fi];i++)
+ if(channels[fi]<2){
+ gtk_widget_set_sensitive(p->chbuttons[i],0);
+ active[i]=0;
+ }else{
+ if(i<ch+2){
+ gtk_widget_set_sensitive(p->chbuttons[i],1);
+ }else{
+ gtk_widget_set_sensitive(p->chbuttons[i],0);
+ active[i]=0;
+ }
+ }
+ ch+=channels[fi];
+ }
+ plot_set_active(PLOT(p->plot),active,bactive);
+ break;
+ }
+
+ /* set labels */
+ switch(plot_link){
+ case LINK_THD:
+ case LINK_THDN:
+ case LINK_THD2:
+ case LINK_THDN2:
+ case LINK_IMPEDENCE_p1:
+ case LINK_IMPEDENCE_1:
+ case LINK_IMPEDENCE_10:
+ case LINK_SUB_REF:
+ ch=0;
+ for(fi=0;fi<inputs;fi++){
+ for(i=ch;i<ch+channels[fi];i++){
+ if(i==ch){
+ gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"reference");
+ }else{
+ sprintf(buf,"channel %d", i-ch);
+ gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),buf);
+ }
+ }
+ ch+=channels[fi];
+ }
+ break;
+ case LINK_SUB_FROM:
+ ch=0;
+ for(fi=0;fi<inputs;fi++){
+ for(i=ch;i<ch+channels[fi];i++){
+ if(i==ch){
+ gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"output");
+ }else{
+ sprintf(buf,"channel %d", i-ch);
+ gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),buf);
+ }
+ }
+ ch+=channels[fi];
+ }
+ break;
+
+ case LINK_INDEPENDENT:
+ case LINK_SUMMED:
+
+ ch=0;
+ for(fi=0;fi<inputs;fi++){
+ for(i=ch;i<ch+channels[fi];i++){
+ sprintf(buf,"channel %d", i-ch);
+ gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),buf);
+ }
+ ch+=channels[fi];
+ }
+ break;
+
+ case LINK_PHASE:
+
+ ch=0;
+ for(fi=0;fi<inputs;fi++){
+ for(i=ch;i<ch+channels[fi];i++){
+ if(channels[fi]<2){
+ gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"unused");
+ }else if(i==ch){
+ gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"response");
+ }else if(i==ch+1){
+ gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"phase");
+ }else{
+ gtk_button_set_label(GTK_BUTTON(p->chbuttons[i]),"unused");
+ }
+ }
+ ch+=channels[fi];
+ }
+ break;
+ }
+
+ /* set colors */
+ switch(plot_link){
+ case LINK_SUMMED:
+ case LINK_SUB_FROM:
+ ch=0;
+ for(fi=0;fi<inputs;fi++){
+ GdkColor rgb = chcolor(ch);
+
+ for(i=ch;i<ch+channels[fi];i++){
+ GtkWidget *button=p->chbuttons[i];
+ set_fg(button,&rgb);
+ }
+ ch+=channels[fi];
+ }
+ break;
+
+ default:
+ ch=0;
+ for(fi=0;fi<inputs;fi++){
+ for(i=ch;i<ch+channels[fi];i++){
+ GdkColor rgb = chcolor(i);
+ GtkWidget *button=p->chbuttons[i];
+ set_fg(button,&rgb);
+ }
+ ch+=channels[fi];
+ }
+ break;
+ }
+
+}
+
+static void reschange(GtkWidget *widget,struct panel *p){
+ plot_res=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+ plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
+}
+
+static void scalechange(GtkWidget *widget,struct panel *p){
+ plot_scale=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+ plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
+}
+
+static void modechange(GtkWidget *widget,struct panel *p){
+ plot_mode=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+ replot(p);
+ plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
+}
+
+static void linkchange(GtkWidget *widget,struct panel *p){
+ plot_link=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+ replot(p);
+ plot_setting(PLOT(p->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
+ chlabels(widget,p);
+}
+
+static void runchange(GtkWidget *widget,struct panel *p){
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))){
+ if(!process_active){
+ pthread_t thread_id;
+ process_active=1;
+ process_exit=0;
+ animate_fish(p);
+ pthread_create(&thread_id,NULL,&process_thread,NULL);
+ }
+ }else{
+ process_exit=1;
+ while(process_active)sched_yield();
+ }
+}
+
+static void holdchange(GtkWidget *widget,struct panel *p){
+ plot_hold=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+ replot(p);
+ plot_draw(PLOT(p->plot));
+}
+
+static void loopchange(GtkWidget *widget,struct panel *p){
+ acc_loop=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+}
+
+static void clearchange(GtkWidget *widget,struct panel *p){
+ acc_clear=1;
+ plot_clear(PLOT(p->plot));
+ if(!process_active){
+ rundata_clear();
+ }
+}
+
+static void rewindchange(GtkWidget *widget,struct panel *p){
+ acc_rewind=1;
+}
+
+extern char *version;
+void panel_create(struct panel *panel){
+ int i;
+
+ GtkWidget *topplace,*topal,*topalb;
+
+ GtkWidget *topframe=gtk_frame_new (NULL);
+ GtkWidget *toplabel=gtk_label_new (NULL);
+ GtkWidget *quitbutton=gtk_button_new_with_mnemonic("_quit");
+ GtkWidget *mainbox=gtk_hbox_new(0,6);
+ GdkWindow *root=gdk_get_default_root_window();
+ GtkWidget *rightbox=gtk_vbox_new(0,0);
+ GtkWidget *leftbox=gtk_vbox_new(0,6);
+
+ panel->toplevel=gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ panel->group = gtk_accel_group_new ();
+ gtk_window_add_accel_group (GTK_WINDOW(panel->toplevel), panel->group);
+
+ char versionmarkup[240];
+ snprintf(versionmarkup,240," <span size=\"large\" weight=\"bold\" "
+ "style=\"italic\" foreground=\"dark blue\">"
+ "Spectrum Analyzer</span> <span size=\"small\" foreground=\"#606060\">"
+ "revision %s</span> ",
+ version);
+
+ /* the Fucking Fish */
+ for(i=0;i<19;i++)
+ panel->ff[i]=gdk_pixmap_create_from_xpm_d(root,
+ panel->fb+i,NULL,ff_xpm[i]);
+ panel->twirlimage=gtk_image_new_from_pixmap(panel->ff[0],panel->fb[0]);
+
+ active = calloc(total_ch,sizeof(*active));
+
+ topplace=gtk_table_new(1,1,0);
+ topalb=gtk_hbox_new(0,0);
+ topal=gtk_alignment_new(1,0,0,0);
+
+ gtk_widget_set_name(quitbutton,"quitbutton");
+
+ gtk_box_pack_start(GTK_BOX(topalb),quitbutton,0,0,0);
+ gtk_container_add (GTK_CONTAINER(topal),topalb);
+
+ gtk_table_attach_defaults(GTK_TABLE(topplace),
+ topal,0,1,0,1);
+ gtk_table_attach_defaults(GTK_TABLE(topplace),
+ topframe,0,1,0,1);
+
+ gtk_container_add (GTK_CONTAINER (panel->toplevel), topplace);
+ gtk_container_set_border_width (GTK_CONTAINER (quitbutton), 3);
+
+ g_signal_connect (G_OBJECT (quitbutton), "clicked",
+ G_CALLBACK (shutdown), NULL);
+ gtk_widget_add_accelerator (quitbutton, "activate", panel->group, GDK_q, 0, 0);
+
+ gtk_container_set_border_width (GTK_CONTAINER (topframe), 3);
+ gtk_container_set_border_width (GTK_CONTAINER (mainbox), 3);
+ gtk_frame_set_shadow_type(GTK_FRAME(topframe),GTK_SHADOW_ETCHED_IN);
+ gtk_frame_set_label_widget(GTK_FRAME(topframe),toplabel);
+ gtk_label_set_markup(GTK_LABEL(toplabel),versionmarkup);
+
+ gtk_container_add (GTK_CONTAINER(topframe), mainbox);
+
+ g_signal_connect (G_OBJECT (panel->toplevel), "delete_event",
+ G_CALLBACK (shutdown), NULL);
+
+ /* add the spectrum plot box */
+ panel->plot=plot_new(blocksize/2+1,inputs,channels,rate);
+ gtk_box_pack_end(GTK_BOX(leftbox),panel->plot,1,1,0);
+ gtk_box_pack_start(GTK_BOX(mainbox),leftbox,1,1,0);
+
+ /*fish */
+ {
+ GtkWidget *box=gtk_hbox_new(1,1);
+ GtkWidget *fishbox=gtk_hbox_new(0,0);
+ gtk_box_pack_end(GTK_BOX(fishbox),panel->twirlimage,0,0,0);
+ gtk_container_set_border_width (GTK_CONTAINER (fishbox), 3);
+
+ gtk_box_pack_start(GTK_BOX(box),fishbox,0,0,0);
+ gtk_box_pack_start(GTK_BOX(rightbox),box,0,0,0);
+ }
+
+ /* rate */
+ /* channels */
+ /* bits */
+ {
+ int fi;
+ int ch=0;
+ char buffer[160];
+ GtkWidget *label;
+ //GtkWidget *vbox=gtk_vbox_new(1,1);
+
+ GtkWidget *sep=gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(rightbox),sep,0,0,6);
+
+ panel->chbuttons = calloc(total_ch,sizeof(*panel->chbuttons));
+ for(fi=0;fi<inputs;fi++){
+
+ char *lastslash = strrchr(inputname[fi],'/');
+ sprintf(buffer,"%s",(lastslash?lastslash+1:inputname[fi]));
+ label=gtk_label_new(buffer);
+ gtk_widget_set_name(label,"readout");
+ gtk_box_pack_start(GTK_BOX(rightbox),label,0,0,0);
+
+ sprintf(buffer,"%dHz %dbit",rate[fi],bits[fi]);
+ label=gtk_label_new(buffer);
+ gtk_widget_set_name(label,"readout");
+ gtk_box_pack_start(GTK_BOX(rightbox),label,0,0,0);
+
+ for(i=ch;i<ch+channels[fi];i++){
+ GtkWidget *button=panel->chbuttons[i]=gtk_toggle_button_new();
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),1);
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (chlabels), panel);
+ gtk_box_pack_start(GTK_BOX(rightbox),button,0,0,0);
+ }
+
+ GtkWidget *sep=gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(rightbox),sep,0,0,6);
+
+ ch+=channels[fi];
+
+ }
+ chlabels(NULL,panel);
+ }
+
+ GtkWidget *bbox=gtk_vbox_new(0,0);
+
+ /* add the action buttons */
+ /* scale */
+ {
+ GtkWidget *menu=gtk_combo_box_new_text();
+ char *entries[]={"single-pixel","1/24th octave","1/12th octave","1/3 octave"};
+ for(i=0;i<4;i++)
+ gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(menu),plot_res);
+ gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+ g_signal_connect (G_OBJECT (menu), "changed",
+ G_CALLBACK (reschange), panel);
+ }
+
+ {
+ GtkWidget *menu=gtk_combo_box_new_text();
+ char *entries[]={"log scale","ISO log scale","linear scale"};
+ for(i=0;i<3;i++)
+ gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(menu),plot_scale);
+ plot_setting(PLOT(panel->plot),plot_res,plot_scale,plot_mode,plot_link,plot_depth,plot_noise);
+ gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+
+ g_signal_connect (G_OBJECT (menu), "changed",
+ G_CALLBACK (scalechange), panel);
+ }
+
+ /* depth */
+ {
+ GtkWidget *menu=gtk_combo_box_new_text();
+ char *entries[]={"10dB","20dB","45dB","90dB","140dB"};
+ for(i=0;i<5;i++)
+ gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(menu),3);
+ gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+
+ g_signal_connect (G_OBJECT (menu), "changed",
+ G_CALLBACK (depthchange), panel);
+ }
+
+ /* mode */
+ {
+ GtkWidget *menu=gtk_combo_box_new_text();
+ char *entries[]={"realtime","maximum","accumulate"};
+ for(i=0;i<3;i++)
+ gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(menu),plot_mode);
+ gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+
+ g_signal_connect (G_OBJECT (menu), "changed",
+ G_CALLBACK (modechange), panel);
+ }
+
+ /* link */
+ {
+ GtkWidget *menu=gtk_combo_box_new_text();
+ for(i=0;i<LINKS;i++)
+ gtk_combo_box_append_text (GTK_COMBO_BOX (menu), link_entries[i]);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
+ gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+
+ g_signal_connect (G_OBJECT (menu), "changed",
+ G_CALLBACK (linkchange), panel);
+ }
+
+ {
+ GtkWidget *sep=gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(bbox),sep,0,0,4);
+ }
+
+ /* run/pause */
+ {
+ GtkWidget *button=gtk_toggle_button_new_with_mnemonic("_run");
+ gtk_widget_add_accelerator (button, "activate", panel->group, GDK_space, 0, 0);
+ gtk_widget_add_accelerator (button, "activate", panel->group, GDK_r, 0, 0);
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (runchange), panel);
+ gtk_box_pack_start(GTK_BOX(bbox),button,0,0,0);
+ panel->run=button;
+ }
+
+ /* hold */
+ /* loop */
+ {
+ GtkWidget *box=gtk_hbox_new(1,1);
+ GtkWidget *button=gtk_toggle_button_new_with_mnemonic("_hold");
+ gtk_widget_add_accelerator (button, "activate", panel->group, GDK_h, 0, 0);
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (holdchange), panel);
+ gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
+
+ button=gtk_toggle_button_new_with_mnemonic("_loop");
+ gtk_widget_add_accelerator (button, "activate", panel->group, GDK_l, 0, 0);
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (loopchange), panel);
+ gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
+ gtk_widget_set_sensitive(button,global_seekable);
+
+ gtk_box_pack_start(GTK_BOX(bbox),box,0,0,0);
+ }
+
+ /* clear */
+ /* rewind */
+ {
+ GtkWidget *box=gtk_hbox_new(1,1);
+ GtkWidget *button=gtk_button_new_with_mnemonic("_clear");
+ gtk_widget_add_accelerator (button, "activate", panel->group, GDK_c, 0, 0);
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (clearchange), panel);
+ gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
+
+ button=gtk_button_new_with_mnemonic("re_wind");
+ gtk_widget_add_accelerator (button, "activate", panel->group, GDK_w, 0, 0);
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (rewindchange), panel);
+ gtk_widget_set_sensitive(button,global_seekable);
+ gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
+
+ gtk_box_pack_start(GTK_BOX(bbox),box,0,0,0);
+ }
+
+ /* dump */
+ {
+ GtkWidget *button=gtk_button_new_with_mnemonic("_dump data");
+ gtk_widget_add_accelerator (button, "activate", panel->group, GDK_d, 0, 0);
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (dump), panel);
+ gtk_box_pack_start(GTK_BOX(bbox),button,0,0,0);
+ }
+
+ /* noise floor */
+ {
+ GtkWidget *button=gtk_toggle_button_new_with_mnemonic("sample _noise floor");
+ gtk_widget_add_accelerator (button, "activate", panel->group, GDK_n, 0, 0);
+ g_signal_connect (G_OBJECT (button), "toggled", G_CALLBACK (noise), panel);
+ gtk_box_pack_start(GTK_BOX(bbox),button,0,0,0);
+ }
+
+ gtk_box_pack_end(GTK_BOX(rightbox),bbox,0,0,0);
+ gtk_box_pack_start(GTK_BOX(mainbox),rightbox,0,0,0);
+
+
+ gtk_widget_show_all(panel->toplevel);
+ //gtk_window_set_resizable(GTK_WINDOW(panel->toplevel),0);
+
+}
+
+static gboolean async_event_handle(GIOChannel *channel,
+ GIOCondition condition,
+ gpointer data){
+ struct panel *panel=data;
+ char buf[1];
+
+ /* read all pending */
+ while(read(eventpipe[0],buf,1)>0);
+
+ increment_fish=1;
+
+ /* check playback status and update the run button if needed */
+ if(process_active && panel->run &&
+ !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel->run)))
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel->run),1);
+ if(!process_active && panel->run &&
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel->run)))
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel->run),0);
+
+ /* update the spectral display; send new data */
+ pthread_mutex_lock(&feedback_mutex);
+ if(plot_last_update!=feedback_increment){
+ pthread_mutex_unlock(&feedback_mutex);
+ replot(panel);
+ plot_draw(PLOT(panel->plot));
+
+ while (gtk_events_pending())
+ gtk_main_iteration();
+ }else
+ pthread_mutex_unlock(&feedback_mutex);
+
+ return TRUE;
+}
+
+static int look_for_gtkrc(char *filename){
+ FILE *f=fopen(filename,"r");
+ if(!f)return 0;
+ fprintf(stderr,"Loading spectrum-gtkrc file found at %s\n",filename);
+ gtk_rc_add_default_file(filename);
+ return 1;
+}
+
+void panel_go(int argc,char *argv[]){
+ char *homedir=getenv("HOME");
+ int found=0;
+ memset(&p,0,sizeof(p));
+
+ found|=look_for_gtkrc(ETCDIR"/spectrum-gtkrc");
+ {
+ char *rcdir=getenv("HOME");
+ if(rcdir){
+ char *rcfile="/.spectrum/spectrum-gtkrc";
+ char *homerc=calloc(1,strlen(rcdir)+strlen(rcfile)+1);
+ strcat(homerc,homedir);
+ strcat(homerc,rcfile);
+ found|=look_for_gtkrc(homerc);
+ }
+ }
+ {
+ char *rcdir=getenv("SPECTRUM_RCDIR");
+ if(rcdir){
+ char *rcfile="/spectrum-gtkrc";
+ char *homerc=calloc(1,strlen(rcdir)+strlen(rcfile)+1);
+ strcat(homerc,homedir);
+ strcat(homerc,rcfile);
+ found|=look_for_gtkrc(homerc);
+ }
+ }
+ found|=look_for_gtkrc("./spectrum-gtkrc");
+
+ if(!found){
+
+ fprintf(stderr,"Could not find the spectrum-gtkrc configuration file normally\n"
+ "installed in one of the following places:\n"
+
+ "\t./spectrum-gtkrc\n"
+ "\t$(SPECTRUM_RCDIR)/spectrum-gtkrc\n"
+ "\t~/.spectrum/spectrum-gtkrc\n\t"
+ ETCDIR"/spectrum-gtkrc\n"
+ "This configuration file is used to tune the color, font and other detail aspects\n"
+ "of the user interface. Although the analyzer will work without it, the UI\n"
+ "appearence will likely make the application harder to use due to missing visual\n"
+ "cues.\n");
+ }
+
+ gtk_rc_add_default_file(ETCDIR"/spectrum-gtkrc");
+ if(homedir){
+ char *rcfile="/.spectrum-gtkrc";
+ char *homerc=calloc(1,strlen(homedir)+strlen(rcfile)+1);
+ strcat(homerc,homedir);
+ strcat(homerc,rcfile);
+ gtk_rc_add_default_file(homerc);
+ }
+ gtk_rc_add_default_file(".spectrum-gtkrc");
+ gtk_rc_add_default_file("spectrum-gtkrc");
+ gtk_init (&argc, &argv);
+
+ panel_create(&p);
+ animate_fish(&p);
+
+ /* set up watching the event pipe */
+ {
+ GIOChannel *channel = g_io_channel_unix_new (eventpipe[0]);
+ guint id;
+
+ g_io_channel_set_encoding (channel, NULL, NULL);
+ g_io_channel_set_buffered (channel, FALSE);
+ g_io_channel_set_close_on_unref (channel, TRUE);
+
+ id = g_io_add_watch (channel, G_IO_IN, async_event_handle, &p);
+
+ g_io_channel_unref (channel);
+
+ }
+
+ /* we want to be running by default */
+ {
+ pthread_t thread_id;
+ animate_fish(&p);
+ process_active=1;
+ pthread_create(&thread_id,NULL,&process_thread,NULL);
+ }
+
+ gtk_main ();
+
+}
+
Added: trunk/spectrum/spec_plot.c
===================================================================
--- trunk/spectrum/spec_plot.c (rev 0)
+++ trunk/spectrum/spec_plot.c 2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,1065 @@
+/*
+ *
+ * gtk2 spectrum analyzer
+ *
+ * Copyright (C) 2004 Monty
+ *
+ * This analyzer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The analyzer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Postfish; see the file COPYING. If not, write to the
+ * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#include "analyzer.h"
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include "spec_plot.h"
+
+static double log_lfreqs[6]={1.,10.,100.,1000.,10000.,100000};
+static double log_tfreqs[37]={5.,6.,7.,8.,9.,20.,30.,40.,50.,60.,70.,80.,90.
+ ,200.,300.,400.,500.,600.,700.,800.,900.,
+ 2000.,3000.,4000.,5000.,6000.,7000.,8000.,9000.,
+ 20000.,30000,40000,50000,60000,70000,80000,90000};
+
+static double iso_lfreqs[12]={31.,63.,125.,250.,500.,1000.,2000.,4000.,8000.,
+ 16000.,32000., 64000.};
+static double iso_tfreqs[24]={25.,40.,50.,80.,100.,160.,200.,315.,400.,630.,
+ 800.,1250.,1600.,2500.,3150.,5000.,6300.,10000.,
+ 12500.,20000.,25000.,40000.,50000.,80000.};
+
+static GtkDrawingAreaClass *parent_class = NULL;
+
+static void compute_imp_scale(GtkWidget *widget){
+ Plot *p=PLOT(widget);
+ int height=widget->allocation.height-p->pady;
+ int i;
+ double lfreqs[9]={10000000.,1000000.,100000.,10000.,1000.,100.,10.,1.,.1};
+ double tfreqs[64]={9000000.,8000000.,7000000.,6000000.,
+ 5000000.,4000000.,3000000.,2000000.,
+ 900000.,800000.,700000.,600000.,
+ 500000.,400000.,300000.,200000.,
+ 90000.,80000.,70000.,60000.,
+ 50000.,40000.,30000.,20000.,
+ 9000.,8000.,7000.,6000.,
+ 5000.,4000.,3000.,2000.,
+ 900.,800.,700.,600.,
+ 500.,400.,300.,200.,
+ 90.,80.,70.,60.,
+ 50.,40.,30.,20,
+ 9.,8.,7.,6.,
+ 5.,4.,3.,2.,
+ .9,.8,.7,.6,
+ .5,.4,.3,.2};
+
+ for(i=0;i<9;i++)
+ p->ygrid[i]=rint( (log10(p->disp_ymax)-log10(lfreqs[i]))/(log10(p->disp_ymax)-log10(.1)) * (height-1));
+ for(i=0;i<64;i++)
+ p->ytic[i]=rint( (log10(p->disp_ymax)-log10(tfreqs[i]))/(log10(p->disp_ymax)-log10(.1)) * (height-1));
+ p->ygrids=9;
+ p->ytics=64;
+
+}
+
+static void compute_metadata(GtkWidget *widget){
+ Plot *p=PLOT(widget);
+ int width=widget->allocation.width-p->padx;
+ int rate=p->maxrate;
+ int nyq=p->maxrate/2.;
+ int i;
+
+ p->xgrids=0;
+ p->xtics=0;
+
+ /* find the places to plot the x grid lines according to scale */
+ switch(p->scale){
+ case 0: /* log */
+ {
+ for(i=0;i<6;i++){
+ if(log_lfreqs[i]<(nyq-.1))
+ p->xgrids=i+1;
+ }
+ for(i=0;i<37;i++){
+ if(log_tfreqs[i]<(nyq-.1))
+ p->xtics=i+1;
+ }
+
+ for(i=0;i<p->xgrids;i++)
+ p->xgrid[i]=rint( (log10(log_lfreqs[i])-log10(5.))/(log10(nyq)-log10(5.)) * (width-1))+p->padx;
+ for(i=0;i<p->xtics;i++)
+ p->xtic[i]=rint( (log10(log_tfreqs[i])-log10(5.))/(log10(nyq)-log10(5.)) * (width-1))+p->padx;
+ }
+
+ break;
+ case 1: /* ISO log */
+ {
+ for(i=0;i<12;i++){
+ if(iso_lfreqs[i]<(nyq-.1))
+ p->xgrids=i+1;
+ }
+ for(i=0;i<24;i++){
+ if(iso_tfreqs[i]<(nyq-.1))
+ p->xtics=i+1;
+ }
+
+ for(i=0;i<p->xgrids;i++)
+ p->xgrid[i]=rint( (log2(iso_lfreqs[i])-log2(25.))/(log2(nyq)-log2(25.)) * (width-1))+p->padx;
+ for(i=0;i<p->xtics;i++)
+ p->xtic[i]=rint( (log2(iso_tfreqs[i])-log2(25.))/(log2(nyq)-log2(25.)) * (width-1))+p->padx;
+ }
+
+ break;
+ case 2: /* linear spacing */
+ {
+ int j;
+ for(i=0;;i++){
+ if(i*p->lin_major >= nyq-.1 || i*p->lin_major>=100000-.1)
+ break;
+ p->xgrids=i+1;
+ }
+ for(i=0;;i++){
+ if(i*p->lin_minor >= nyq-.1 || i*p->lin_minor>=100000-.1)
+ break;
+ if(i%p->lin_mult!=0)
+ p->xtics++;
+ }
+
+ for(i=0;i<p->xgrids;i++){
+ double lfreq=i*p->lin_major;
+ p->xgrid[i]=rint(lfreq/nyq * (width-1))+p->padx;
+ }
+ j=0;
+ for(i=0;i<p->xtics;i++,j++){
+ double lfreq;
+ if(j%p->lin_mult==0)j++;
+ lfreq=j*p->lin_minor;
+ p->xtic[i]=rint(lfreq/nyq * (width-1))+p->padx;
+ }
+ }
+ break;
+ }
+}
+
+GdkColor chcolor(int ch){
+ GdkColor rgb={0,0,0,0};
+
+ switch(ch%7){
+ case 0:
+ rgb.red=0x4000;
+ rgb.green=0x4000;
+ rgb.blue=0x4000;
+ break;
+ case 1:
+ rgb.red=0xd000;
+ rgb.green=0x0000;
+ rgb.blue=0x0000;
+ break;
+ case 2:
+ rgb.red=0x0000;
+ rgb.green=0xb000;
+ rgb.blue=0x0000;
+ break;
+ case 3:
+ rgb.red=0x0000;
+ rgb.green=0x0000;
+ rgb.blue=0xf000;
+ break;
+ case 4:
+ rgb.red=0xc000;
+ rgb.green=0xc000;
+ rgb.blue=0x0000;
+ break;
+ case 5:
+ rgb.red=0x0000;
+ rgb.green=0xc000;
+ rgb.blue=0xc000;
+ break;
+ case 6:
+ rgb.red=0xc000;
+ rgb.green=0x0000;
+ rgb.blue=0xe000;
+ break;
+ }
+
+ return rgb;
+}
+
+static void draw(GtkWidget *widget){
+ int i;
+ Plot *p=PLOT(widget);
+ int height=widget->allocation.height;
+ int width=widget->allocation.width;
+ GtkWidget *parent=gtk_widget_get_parent(widget);
+ int impedence = (p->link == LINK_IMPEDENCE_p1 ||
+ p->link == LINK_IMPEDENCE_1 ||
+ p->link == LINK_IMPEDENCE_10);
+ int phase = (p->link == LINK_PHASE);
+ int padx = p->padx;
+
+ if(phase){
+ /* are any of the phase channels actually active? */
+ int gi;
+ int ch=0;
+
+ phase = 0;
+ for(gi=0;gi<p->groups && !phase;gi++){
+ if(p->ch_active[ch+1]){
+ phase=1;
+ break;
+ }
+
+ ch+=p->ch[gi];
+ }
+ }
+
+ if(!p->drawgc){
+ p->drawgc=gdk_gc_new(p->backing);
+ gdk_gc_copy(p->drawgc,widget->style->black_gc);
+ }
+ if(!p->dashes){
+ p->dashes=gdk_gc_new(p->backing);
+ gdk_gc_copy(p->dashes, p->drawgc);
+ gdk_gc_set_line_attributes(p->dashes, 1, GDK_LINE_ON_OFF_DASH, GDK_CAP_BUTT, GDK_JOIN_MITER);
+ gdk_gc_set_dashes(p->dashes,0,"\002\002",2);
+ }
+
+ /* clear the old rectangle out */
+ {
+ GdkGC *gc=parent->style->bg_gc[0];
+ gdk_draw_rectangle(p->backing,gc,1,0,0,padx,height);
+ gdk_draw_rectangle(p->backing,gc,1,0,height-p->pady,width,p->pady);
+
+ gc=parent->style->white_gc;
+ gdk_draw_rectangle(p->backing,gc,1,padx,0,width-padx,height-p->pady+1);
+ }
+
+ /* draw the noise floor if active */
+ if(p->floor){
+ GdkColor rgb = {0,0xd000,0xd000,0xd000};
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+ for(i=0;i<width-padx;i++){
+ float val=p->floor[i];
+ int y;
+
+ /* No noise floor is passed back for display in the modes where it's irrelevant */
+ y= rint((height-p->pady-1)/p->disp_depth*(p->disp_ymax-val));
+ if(y<height-p->pady)
+ gdk_draw_line(p->backing,p->drawgc,padx+i,y,padx+i,height-p->pady-1);
+ }
+ }
+
+ /* draw the light x grid */
+ {
+ int i;
+ GdkColor rgb={0,0,0,0};
+
+ rgb.red=0xc000;
+ rgb.green=0xff00;
+ rgb.blue=0xff00;
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+ for(i=0;i<p->xtics;i++)
+ gdk_draw_line(p->backing,p->drawgc,p->xtic[i],0,p->xtic[i],height-p->pady);
+ }
+
+ /* draw the x labels */
+ {
+ PangoLayout **proper;
+ switch(p->scale){
+ case 0:
+ proper=p->log_layout;
+ break;
+ case 1:
+ proper=p->iso_layout;
+ break;
+ case 2:
+ proper=p->lin_layout;
+ break;
+ }
+ for(i=0;i<p->xgrids;i++){
+ int px,py;
+ pango_layout_get_pixel_size(proper[i],&px,&py);
+
+ gdk_draw_layout (p->backing,
+ widget->style->black_gc,
+ p->xgrid[i]-(px/2), height-py+2,
+ proper[i]);
+ }
+ }
+
+ /* draw the light y grid */
+ if(impedence){ /* impedence mode */
+
+ GdkColor rgb={0,0,0,0};
+ rgb.red=0xc000;
+ rgb.green=0xff00;
+ rgb.blue=0xff00;
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+ compute_imp_scale(widget);
+
+ for(i=0;i<p->ytics;i++)
+ gdk_draw_line(p->backing,p->drawgc,padx,p->ytic[i],width,p->ytic[i]);
+
+ }else{
+ float del=(height-p->pady-1)/(float)p->disp_depth,off;
+ int i,half=0;
+ int max,mul;
+ GdkColor rgb={0,0,0,0};
+
+ {
+ if(del>16){
+ half=1;
+ max=303;
+ mul=1;
+ off=(p->disp_ymax-ceil(p->disp_ymax))*2;
+ del*=.5;
+ }else if(del>8){
+ max=151;
+ mul=1;
+ off=p->disp_ymax-ceil(p->disp_ymax);
+ }else if(del*2>8){
+ max=76;
+ mul=2;
+ off=p->disp_ymax-ceil(p->disp_ymax*.5)*2;
+ }else{
+ max=31;
+ mul=5;
+ off=p->disp_ymax-ceil(p->disp_ymax*.2)*5;
+ }
+
+ rgb.red=0xc000;
+ rgb.green=0xff00;
+ rgb.blue=0xff00;
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+ gdk_gc_set_rgb_fg_color(p->dashes,&rgb);
+
+ for(i=0;i<max;i+=2){
+ int ymid=rint(del * (i * mul + off));
+ if(ymid>=0 && ymid<height-p->pady)
+ gdk_draw_line(p->backing,p->drawgc,padx,ymid,width,ymid);
+ }
+
+ for(i=1;i<max;i+=2){
+ int ymid=rint(del * (i * mul + off));
+ if(ymid>=0 && ymid<height-p->pady)
+ gdk_draw_line(p->backing,(half?p->dashes:p->drawgc),padx,ymid,width,ymid);
+ }
+ }
+ }
+
+ /* dark x grid */
+ {
+ int i;
+ GdkColor rgb={0,0,0,0};
+
+ rgb.red=0x0000;
+ rgb.green=0xc000;
+ rgb.blue=0xc000;
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+ for(i=0;i<p->xgrids;i++)
+ gdk_draw_line(p->backing,p->drawgc,p->xgrid[i],0,p->xgrid[i],height-p->pady);
+ }
+
+ /* dark y grid */
+ if(impedence){
+ GdkColor rgb={0,0,0,0};
+ rgb.red=0x0000;
+ rgb.green=0xc000;
+ rgb.blue=0xc000;
+
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+ for(i=0;i<p->ygrids;i++){
+ int px,py;
+ pango_layout_get_pixel_size(p->imp_layout[i],&px,&py);
+
+ gdk_draw_layout (p->backing,
+ widget->style->black_gc,
+ padx-px-2, p->ygrid[i]-py/2,
+ p->imp_layout[i]);
+
+ gdk_draw_line(p->backing,p->drawgc,padx,p->ygrid[i],width,p->ygrid[i]);
+ }
+
+ }else{
+ GdkColor rgb={0,0,0,0};
+ int label=ceil(p->disp_ymax/5+28),i;
+ float del=(height-p->pady-1)/(float)p->disp_depth,step;
+ float off=p->disp_ymax-ceil(p->disp_ymax*.2)*5;
+ step=2;
+ if(del>8)step=1;
+
+ for(i=0;i<32;i++){
+ if(((label-i)&1)==0 || step==1){
+ int ymid=rint(del * (i*5+off));
+ int px,py;
+
+ if(((label-i)&1)==0){
+ rgb.red=0x0000;
+ rgb.green=0xc000;
+ rgb.blue=0xc000;
+ }else{
+ rgb.red=0xa000;
+ rgb.green=0xe000;
+ rgb.blue=0xe000;
+ }
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+ if(label-i>=0 && label-i<57 && ymid>=0 && ymid<height-p->pady){
+ pango_layout_get_pixel_size(p->db_layout[label-i],&px,&py);
+
+ gdk_draw_layout (p->backing,
+ widget->style->black_gc,
+ padx-px-2, ymid-py/2,
+ p->db_layout[label-i]);
+ gdk_draw_line(p->backing,p->drawgc,padx,ymid,width,ymid);
+ }
+ }
+ }
+ }
+
+ /* phase? draw in phase and tics on right axis */
+ if(phase){
+ GdkColor rgb={0,0xd000,0x0000,0x0000};
+ float depth = p->disp_pmax-p->disp_pmin;
+ int label=ceil(p->disp_pmax/10+18),i;
+ float del=(height-p->pady-1)/depth,step;
+ float off=p->disp_pmax-ceil(p->disp_pmax*.1)*10;
+ step=2;
+ if(del>8)step=1;
+
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+ for(i=0;i<37;i++){
+ if(((label-i)&1)==0 || step==1){
+ int ymid=rint(del * (i*10+off));
+ int px,py;
+
+ if(label-i>=0 && label-i<37 && ymid>=0 && ymid<height-p->pady){
+ pango_layout_get_pixel_size(p->phase_layout[label-i],&px,&py);
+
+ gdk_draw_layout (p->backing,p->drawgc,
+ width-p->phax, ymid-py/2,
+ p->phase_layout[label-i]);
+ }
+ }
+ }
+
+ if(del>10){
+ for(i=0;;i++){
+ int ymid=rint(del * (i+off));
+ if(ymid>=height-p->pady)break;
+ if(ymid>=0)
+ gdk_draw_line(p->backing,p->drawgc,width-p->phax-(i%5==0?15:10),ymid,width-p->phax-(i%5==0?5:7),ymid);
+ }
+ }else if(del>5){
+ for(i=0;;i++){
+ int ymid=rint(del * (i*2+off));
+ if(ymid>=height-p->pady)break;
+ if(ymid>=0)
+ gdk_draw_line(p->backing,p->drawgc,width-p->phax-12,ymid,width-p->phax-7,ymid);
+ }
+ } else if(del>2){
+ for(i=0;;i++){
+ int ymid=rint(del * (i*5+off));
+ if(ymid>=height-p->pady)break;
+ if(ymid>=0)
+ gdk_draw_line(p->backing,p->drawgc,width-p->phax-15,ymid,width-p->phax-5,ymid);
+ }
+ }
+
+ for(i=0;;i++){
+ int ymid=rint(del * (i*10+off));
+ if(ymid>=height-p->pady)break;
+ if(ymid>=0){
+ gdk_draw_line(p->backing,p->drawgc,width-p->phax-5,ymid-1,width-p->phax-2,ymid-1);
+ gdk_draw_line(p->backing,p->drawgc,width-p->phax-25,ymid,width-p->phax-2,ymid);
+ gdk_draw_line(p->backing,p->drawgc,width-p->phax-5,ymid+1,width-p->phax-2,ymid+1);
+ }
+ }
+
+ }
+
+
+ {
+ GdkGCValues values;
+ //gdk_gc_get_values(p->drawgc,&values);
+ values.line_width=2;
+ gdk_gc_set_values(p->drawgc,&values,GDK_GC_LINE_WIDTH);
+ }
+
+ /* draw actual data */
+ if(p->ydata){
+ int cho=0;
+ int gi;
+ for(gi=0;gi<p->groups;gi++){
+ int ch;
+ GdkColor rgb;
+
+ for(ch=cho;ch<cho+p->ch[gi];ch++){
+ if(p->ch_active[ch]){
+ int prev;
+ int first=0;
+ float yprev=NAN;
+
+ rgb = chcolor(ch);
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+ for(i=0;i<width-padx;i++){
+ float val=p->ydata[ch][i];
+ int y;
+
+ if(isnan(yprev) || isnan(val)){
+ yprev = val;
+ }else{
+ yprev = val;
+
+ if(impedence){ /* log scale for impedence */
+ y =rint( (log10(p->disp_ymax)-log10(val))/(log10(p->disp_ymax)-log10(.1)) *
+ (height-p->pady-1));
+ }else if(phase && ch==cho+1){
+ y= rint((height-p->pady-1)/(p->disp_pmax-p->disp_pmin)*(p->disp_pmax-val));
+ }else{
+ y= rint((height-p->pady-1)/p->disp_depth*(p->disp_ymax-val));
+ }
+
+ if(first && (y<height-p->pady || prev<height-p->pady)){
+ int ly = y;
+ int lp = prev;
+
+ if(ly>=height-p->pady)ly=height-p->pady;
+ if(lp>=height-p->pady)lp=height-p->pady;
+
+ gdk_draw_line(p->backing,p->drawgc,padx+i-1,lp,padx+i,ly);
+
+ ly++;
+ lp++;
+
+ if(ly>=height-p->pady)ly=height-p->pady;
+ if(lp>=height-p->pady)lp=height-p->pady;
+
+ gdk_draw_line(p->backing,p->drawgc,padx+i-1,lp,padx+i,ly);
+ }
+ first=1;
+ prev=y;
+ }
+
+
+ }
+ }
+ }
+ cho+=p->ch[gi];
+ }
+ }
+
+ {
+ GdkGCValues values;
+ //gdk_gc_get_values(p->drawgc,&values);
+ values.line_width=1;
+ gdk_gc_set_values(p->drawgc,&values,GDK_GC_LINE_WIDTH);
+ }
+
+
+}
+
+static void draw_and_expose(GtkWidget *widget){
+ Plot *p=PLOT(widget);
+ if(!GDK_IS_DRAWABLE(p->backing))return;
+ draw(widget);
+ if(!GTK_WIDGET_DRAWABLE(widget))return;
+ if(!GDK_IS_DRAWABLE(widget->window))return;
+ gdk_draw_drawable(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ p->backing,
+ 0, 0,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+}
+
+static gboolean expose( GtkWidget *widget, GdkEventExpose *event ){
+ Plot *p=PLOT(widget);
+ gdk_draw_drawable(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ p->backing,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ return FALSE;
+}
+
+static void size_request (GtkWidget *widget,GtkRequisition *requisition){
+ Plot *p=PLOT(widget);
+ requisition->width = 400;
+ requisition->height = 400;
+ int axisy=0,axisx=0,pady=0,padx=0,phax=0,px,py,i;
+
+ /* find max lin layout */
+ {
+ int max=0;
+ int maxy=0;
+ for(i=0;p->lin_layout[i];i++){
+ if(p->lin_major*i >= p->maxrate/2-.1)break;
+ pango_layout_get_pixel_size(p->lin_layout[i],&px,&py);
+ if(px>max)max=px;
+ if(py>pady)pady=py;
+ if(py>maxy)maxy=py;
+ }
+ max+=maxy*1.5;
+ max*=i+1;
+ if(axisx<max)axisx=max;
+ }
+ /* find max log layout */
+ {
+ int max=0;
+ int maxy=0;
+ for(i=0;p->log_layout[i];i++){
+ if(log_lfreqs[i] >= p->maxrate/2-.1)break;
+ pango_layout_get_pixel_size(p->log_layout[i],&px,&py);
+ if(px>max)max=px;
+ if(py>pady)pady=py;
+ if(py>maxy)maxy=py;
+ }
+ max+=maxy*1.5;
+ max*=i+1;
+ if(axisx<max)axisx=max;
+ }
+ /* find max iso layout */
+ {
+ int max=0;
+ int maxy=0;
+ for(i=0;p->iso_layout[i];i++){
+ if(iso_lfreqs[i] >= p->maxrate/2-.1)break;
+ pango_layout_get_pixel_size(p->iso_layout[i],&px,&py);
+ if(px>max)max=px;
+ if(py>pady)pady=py;
+ if(py>maxy)maxy=py;
+ }
+ max+=maxy*1.5;
+ max*=i+1;
+ if(axisx<max)axisx=max;
+ }
+ /* find max db layout */
+ {
+ int max=0;
+ for(i=0;p->db_layout[i];i++){
+ pango_layout_get_pixel_size(p->db_layout[i],&px,&py);
+ if(py>max)max=py;
+ if(px>padx)padx=px;
+ }
+ axisy=(max)*8;
+ if(axisy<max)axisy=max;
+ }
+ /* find max imped layout */
+ {
+ int max=0;
+ for(i=0;p->imp_layout[i];i++){
+ pango_layout_get_pixel_size(p->imp_layout[i],&px,&py);
+ if(py>max)max=py;
+ if(px>padx)padx=px;
+ }
+ axisy=(max)*8;
+ if(axisy<max)axisy=max;
+ }
+ /* find max phase layout */
+ {
+ int max=0;
+ for(i=0;p->phase_layout[i];i++){
+ pango_layout_get_pixel_size(p->phase_layout[i],&px,&py);
+ if(py>max)max=py;
+ if(px>phax)phax=px;
+ }
+ axisy=(max)*8;
+ if(axisy<max)axisy=max;
+ }
+
+ if(requisition->width<axisx+padx)requisition->width=axisx+padx;
+ if(requisition->height<axisy+pady)requisition->height=axisy+pady;
+ p->padx=padx;
+ p->pady=pady;
+ p->phax=phax;
+}
+
+static gboolean configure(GtkWidget *widget, GdkEventConfigure *event){
+ Plot *p=PLOT(widget);
+
+ if (p->backing)
+ g_object_unref(p->backing);
+
+ p->backing = gdk_pixmap_new(widget->window,
+ widget->allocation.width,
+ widget->allocation.height,
+ -1);
+
+ p->ydata=NULL;
+ p->configured=1;
+
+ compute_metadata(widget);
+ plot_refresh(p,NULL);
+ draw_and_expose(widget);
+
+ return TRUE;
+}
+
+static void plot_class_init (PlotClass *class){
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+ parent_class = g_type_class_peek_parent (class);
+
+ widget_class->expose_event = expose;
+ widget_class->configure_event = configure;
+ widget_class->size_request = size_request;
+}
+
+static void plot_init (Plot *p){
+ p->mode=0;
+ p->scale=0;
+ p->depth=45.;
+}
+
+GType plot_get_type (void){
+ static GType m_type = 0;
+ if (!m_type){
+ static const GTypeInfo m_info={
+ sizeof (PlotClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) plot_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (Plot),
+ 0,
+ (GInstanceInitFunc) plot_init,
+ 0
+ };
+
+ m_type = g_type_register_static (GTK_TYPE_DRAWING_AREA, "Plot", &m_info, 0);
+ }
+
+ return m_type;
+}
+
+GtkWidget* plot_new (int size, int groups, int *channels, int *rate){
+ GtkWidget *ret= GTK_WIDGET (g_object_new (plot_get_type (), NULL));
+ Plot *p=PLOT(ret);
+ int g,i;
+ int ch=0;
+ int maxrate=-1;
+ p->groups = groups;
+ for(g=0;g<groups;g++){
+ ch+=channels[g];
+ if(rate[g]>maxrate)maxrate=rate[g];
+ }
+
+ p->total_ch = ch;
+
+ p->ch=channels;
+ p->rate=rate;
+ p->maxrate=maxrate;
+
+ if(maxrate > 100000){
+ p->lin_major = 10000.;
+ p->lin_minor = 2000.;
+ p->lin_mult = 5;
+ }else if(maxrate > 50000){
+ p->lin_major = 5000.;
+ p->lin_minor = 1000.;
+ p->lin_mult = 5;
+ }else{
+ p->lin_major=2000.;
+ p->lin_minor=500.;
+ p->lin_mult=4;
+ }
+
+ /* generate all the text layouts we'll need */
+ /* linear X scale */
+ {
+ if(maxrate>100000){
+ char *labels[11]={"DC","10kHz","20kHz","30kHz","40kHz","50kHz","60kHz",
+ "70kHz","80kHz","90kHz",""};
+ p->lin_layout=calloc(12,sizeof(*p->lin_layout));
+ for(i=0;i<11;i++)
+ p->lin_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+ }else if(maxrate > 50000){
+ char *labels[11]={"DC","5kHz","10kHz","15kHz","20kHz","25kHz","30kHz",
+ "35kHz","40kHz","45kHz",""};
+ p->lin_layout=calloc(12,sizeof(*p->lin_layout));
+ for(i=0;i<11;i++)
+ p->lin_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+ }else{
+ char *labels[14]={"DC","2kHz","4kHz","6kHz","8kHz","10kHz","12kHz",
+ "14kHz","16kHz","18kHz","20kHz","22kHz","24kHz",""};
+ p->lin_layout=calloc(15,sizeof(*p->lin_layout));
+ for(i=0;i<14;i++)
+ p->lin_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+ }
+ }
+ /* phase Y scale */
+ {
+ char *labels[37]={"-180\xC2\xB0","-170\xC2\xB0","-160\xC2\xB0",
+ "-150\xC2\xB0","-140\xC2\xB0","-130\xC2\xB0",
+ "-120\xC2\xB0","-110\xC2\xB0","-100\xC2\xB0",
+ "-90\xC2\xB0","-80\xC2\xB0","-70\xC2\xB0",
+ "-60\xC2\xB0","-50\xC2\xB0","-40\xC2\xB0",
+ "-30\xC2\xB0","-20\xC2\xB0","-10\xC2\xB0",
+ "-0\xC2\xB0","+10\xC2\xB0","+20\xC2\xB0",
+ "+30\xC2\xB0","+40\xC2\xB0","+50\xC2\xB0",
+ "+60\xC2\xB0","+70\xC2\xB0","+80\xC2\xB0",
+ "+90\xC2\xB0","+100\xC2\xB0","+110\xC2\xB0",
+ "+120\xC2\xB0","+130\xC2\xB0","+140\xC2\xB0",
+ "+150\xC2\xB0","+160\xC2\xB0","+170\xC2\xB0",
+ "+180\xC2\xB0"};
+ p->phase_layout=calloc(38,sizeof(*p->phase_layout));
+ for(i=0;i<37;i++)
+ p->phase_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+ }
+ /* log X scale */
+ {
+ char *labels[6]={"1Hz","10Hz","100Hz","1kHz","10kHz",""};
+ p->log_layout=calloc(7,sizeof(*p->log_layout));
+ for(i=0;i<6;i++)
+ p->log_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+ }
+ /* Impedence Y scale */
+ {
+ char *labels[9]={"10M\xCE\xA9","1M\xCE\xA9","100k\xCE\xA9","10k\xCE\xA9",
+ "1k\xCE\xA9","100\xCE\xA9","10\xCE\xA9","1\xCE\xA9",".1\xCE\xA9"};
+ p->imp_layout=calloc(10,sizeof(*p->imp_layout));
+ for(i=0;i<9;i++)
+ p->imp_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+ }
+ /* ISO log X scale */
+ {
+ char *labels[12]={"31Hz","63Hz","125Hz","250Hz","500Hz","1kHz","2kHz",
+ "4kHz","8kHz","16kHz","32kHz","64kHz"};
+ p->iso_layout=calloc(13,sizeof(*p->iso_layout));
+ for(i=0;i<12;i++)
+ p->iso_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+ }
+ /* dB Y scale */
+ {
+ char *labels[57]={"-140dB","-135dB","-130dB","-125dB","-120dB","-115dB",
+ "-110dB","-105dB","-100dB","-95dB","-90dB","-85dB",
+ "-80dB","-75dB","-70dB","-65dB","-60dB","-55dB","-50dB",
+ "-45dB","-40dB","-35dB","-30dB","-25dB","-20dB",
+ "-15dB","-10dB","-5dB","0dB","+5dB","+10dB","+15dB",
+ "+20dB","+25dB","+30dB","+35dB","+40dB","+45dB","+50dB",
+ "+55dB","+60dB","+65dB","+70dB","+75dB","+80dB","+85dB",
+ "+90dB","+95dB","+100dB","+105dB","+110dB","+115dB",
+ "+120dB","+125dB","+130dB","+135dB","+140dB"};
+ p->db_layout=calloc(58,sizeof(*p->db_layout));
+ for(i=0;i<57;i++)
+ p->db_layout[i]=gtk_widget_create_pango_layout(ret,labels[i]);
+ }
+
+ p->ch_active=calloc(ch,sizeof(*p->ch_active));
+ p->ch_process=calloc(ch,sizeof(*p->ch_process));
+
+ plot_clear(p);
+ return ret;
+}
+
+void plot_refresh (Plot *p, int *process){
+ float ymax,pmax,pmin;
+ int width=GTK_WIDGET(p)->allocation.width-p->padx;
+ int height=GTK_WIDGET(p)->allocation.height-p->pady;
+ float **data;
+ float *floor;
+
+ if(!p->configured)return;
+
+ if(process)
+ memcpy(p->ch_process,process,p->total_ch*sizeof(*process));
+
+ data = process_fetch(p->res, p->scale, p->mode, p->link,
+ p->ch_process,width,&ymax,&pmax,&pmin,&floor,p->noise);
+
+ p->ydata=data;
+ if(floor)
+ p->floor=floor;
+ else
+ p->floor=NULL;
+
+ /* graph limit updates are conditional depending on mode/link */
+ pmax+=5;
+ pmin-=5;
+ if(pmax<5)pmax=5;
+ if(pmin>-30)pmin=-30;
+
+ switch(p->link){
+ case LINK_INDEPENDENT:
+ case LINK_SUMMED:
+ case LINK_PHASE:
+ case LINK_THD:
+ case LINK_THDN:
+ {
+ float dBpp = p->depth/height;
+ ymax += dBpp*10;
+ }
+ break;
+ case LINK_IMPEDENCE_p1:
+ case LINK_IMPEDENCE_1:
+ case LINK_IMPEDENCE_10:
+ if(ymax<12)
+ ymax=12;
+ else
+ ymax *=1.8;
+ break;
+ }
+
+ if(p->mode == 0){
+ /* "Instantaneous' mode scale regression is conditional and
+ damped. Start the timer/run the timer while any one scale measure
+ should be dropping by more than 50px. If any peaks occur above,
+ reset timer. Once timer runs out, drop 5px per frame */
+#define PXTHRESH 25
+#define PXDEL 10.
+#define TIMERFRAMES 20
+ if(p->ymax>ymax){
+ float oldzero = (height-1)/p->depth*p->ymax;
+ float newzero = (height-1)/p->depth*ymax;
+
+ if(newzero+PXTHRESH<oldzero){
+ if(p->ymaxtimer){
+ p->ymaxtimer--;
+ }else{
+ p->ymax = (oldzero-PXDEL)*p->depth/(height-1);
+ }
+ }else{
+ p->ymaxtimer = TIMERFRAMES;
+ }
+ }else
+ p->ymaxtimer = TIMERFRAMES;
+
+ if(p->pmax>pmax || p->pmin<pmin){
+ float newmax = (height-1)/(p->pmax-p->pmin)*(p->pmax-pmax);
+ float newmin = (height-1)/(p->pmax-p->pmin)*(pmin-p->pmin);
+
+ if(newmax>PXTHRESH || newmin>PXTHRESH){
+ if(p->phtimer){
+ p->phtimer--;
+ }else{
+ if(newmax>PXTHRESH)
+ p->pmax -= PXDEL/(height-1)*(p->pmax-p->pmin);
+ if(newmin>PXTHRESH)
+ p->pmin += PXDEL/(height-1)*(p->pmax-p->pmin);
+ }
+ }else{
+ p->phtimer = TIMERFRAMES;
+ }
+ }else
+ p->phtimer = TIMERFRAMES;
+ }
+
+ if(ymax<p->depth-140.)ymax=p->depth-140.;
+ if(ymax>140.)ymax=140.;
+ if(pmax>180)pmax=180;
+ if(pmin<-180)pmin=-180;
+
+ if(p->mode == 0){
+ if(ymax>p->ymax)p->ymax=ymax;
+ if(pmax>p->pmax)p->pmax=pmax;
+ if(pmin<p->pmin)p->pmin=pmin;
+ }else{
+ p->ymax=ymax;
+ p->pmax=pmax;
+ p->pmin=pmin;
+ }
+
+ p->disp_depth = p->depth;
+ p->disp_ymax = p->ymax;
+ p->disp_pmax = p->pmax;
+ p->disp_pmin = p->pmin;
+
+ /* finally, align phase/response zeros on phase graphs */
+ if(p->disp_ymax>-140){
+ if(p->link == LINK_PHASE){
+ /* In a phase/response graph, 0dB/0degrees are bound and always on-screen. */
+ float mzero = (height-1)/p->disp_depth*p->disp_ymax;
+ float pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
+
+ if(mzero<pzero){
+ /* straightforward; move the dB range down */
+ p->disp_ymax = pzero*p->disp_depth/(height-1);
+ }else{
+ /* a little harder as phase has a min and a max.
+ First increase the pmax to match the dB zero. */
+ p->disp_pmax = p->disp_pmin/(1-(height-1)/mzero);
+ pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
+
+ /* That worked, but might have run p->max overrange */
+ if(p->disp_pmax>180.){
+ /* only way to reconcile this one is to increase the pdepth */
+ p->disp_pmax = 180.;
+ pzero = (height-1)/(p->disp_pmax-p->disp_pmin)*p->disp_pmax;
+ p->disp_depth = (height-1)/pzero*p->disp_ymax;
+ }
+ }
+ }
+ }
+}
+
+void plot_clear (Plot *p){
+ GtkWidget *widget=GTK_WIDGET(p);
+ int width=GTK_WIDGET(p)->allocation.width-p->padx;
+ int i,j;
+
+ if(p->ydata)
+ for(i=0;i<p->total_ch;i++)
+ for(j=0;j<width;j++)
+ p->ydata[i][j]=NAN;
+ p->ymax=p->depth-140;
+ p->pmax=0;
+ p->pmin=0;
+ draw_and_expose(widget);
+}
+
+float **plot_get (Plot *p){
+ return(p->ydata);
+}
+
+void plot_setting (Plot *p, int res, int scale, int mode, int link, int depth, int noise){
+ GtkWidget *widget=GTK_WIDGET(p);
+ p->res=res;
+ p->scale=scale;
+ p->mode=mode;
+ p->depth=depth;
+ p->link=link;
+ p->noise=noise;
+
+ p->ymax=-140;
+ p->pmax=0;
+ p->pmin=0;
+
+ compute_metadata(widget);
+ plot_refresh(p,NULL);
+ draw_and_expose(widget);
+}
+
+void plot_draw (Plot *p){
+ GtkWidget *widget=GTK_WIDGET(p);
+ draw_and_expose(widget);
+}
+
+void plot_set_active(Plot *p, int *a, int *b){
+ GtkWidget *widget=GTK_WIDGET(p);
+ memcpy(p->ch_active,a,p->total_ch*sizeof(*a));
+ memcpy(p->ch_process,b,p->total_ch*sizeof(*b));
+ plot_refresh(p,NULL);
+ draw_and_expose(widget);
+}
+
Copied: trunk/spectrum/spec_plot.h (from rev 18227, trunk/spectrum/plot.h)
===================================================================
--- trunk/spectrum/spec_plot.h (rev 0)
+++ trunk/spectrum/spec_plot.h 2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,132 @@
+/*
+ *
+ * gtk2 spectrum analyzer
+ *
+ * Copyright (C) 2004 Monty
+ *
+ * This analyzer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The analyzer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Postfish; see the file COPYING. If not, write to the
+ * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#ifndef __PLOT_H__
+#define __PLOT_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkdrawingarea.h>
+
+G_BEGIN_DECLS
+
+#define PLOT_TYPE (plot_get_type ())
+#define PLOT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PLOT_TYPE, Plot))
+#define PLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PLOT_TYPE, PlotClass))
+#define IS_PLOT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PLOT_TYPE))
+#define IS_PLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PLOT_TYPE))
+
+typedef struct _Plot Plot;
+typedef struct _PlotClass PlotClass;
+
+struct _Plot{
+
+ GtkDrawingArea canvas;
+ GdkPixmap *backing;
+ GdkGC *drawgc;
+ GdkGC *dashes;
+
+ PangoLayout **lin_layout;
+ PangoLayout **log_layout;
+ PangoLayout **iso_layout;
+ PangoLayout **db_layout;
+ PangoLayout **imp_layout;
+ PangoLayout **phase_layout;
+
+ int configured;
+ float **ydata;
+ float *floor;
+
+ int groups;
+ int *ch;
+ int *ch_active;
+ int *ch_process;
+ int total_ch;
+ int datasize;
+ int mode;
+ int link;
+ int scale;
+ int res;
+ int noise;
+ int *rate;
+ int maxrate;
+
+ float lin_major;
+ float lin_minor;
+ int lin_mult;
+
+ int xgrid[20];
+ int xgrids;
+ int xtic[200];
+ int xtics;
+
+ int ygrid[11];
+ int ygrids;
+ int ytic[200];
+ int ytics;
+
+ float depth;
+ float ymax;
+ float pmax;
+ float pmin;
+
+ float disp_depth;
+ float disp_ymax;
+ float disp_pmax;
+ float disp_pmin;
+
+ float padx;
+ float phax;
+ float pady;
+
+ int ymaxtimer;
+ int phtimer;
+};
+
+struct _PlotClass{
+
+ GtkDrawingAreaClass parent_class;
+ void (* plot) (Plot *m);
+};
+
+GType plot_get_type (void);
+GtkWidget* plot_new (int n, int inputs, int *channels, int *rate);
+void plot_refresh (Plot *m, int *process);
+void plot_setting (Plot *m, int res, int scale, int mode, int link, int depth, int noise);
+void plot_draw (Plot *m);
+void plot_clear (Plot *m);
+int plot_width (Plot *m);
+float** plot_get (Plot *m);
+void plot_set_active (Plot *m, int *, int *);
+
+GdkColor chcolor(int ch);
+
+G_END_DECLS
+
+#endif
+
+
+
+
Copied: trunk/spectrum/spec_process.c (from rev 18228, trunk/spectrum/process.c)
===================================================================
--- trunk/spectrum/spec_process.c (rev 0)
+++ trunk/spectrum/spec_process.c 2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,914 @@
+/*
+ *
+ * gtk2 spectrum analyzer
+ *
+ * Copyright (C) 2004 Monty
+ *
+ * This analyzer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The analyzer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Postfish; see the file COPYING. If not, write to the
+ * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#include "analyzer.h"
+#include "io.h"
+
+static float *window=NULL;
+static float *freqbuffer=0;
+static fftwf_plan plan;
+
+pthread_mutex_t feedback_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+int feedback_increment=0;
+
+float *feedback_count;
+float **plot_data;
+float *plot_floor=NULL;
+float **work_floor=NULL;
+float *process_work;
+
+float **feedback_acc;
+float **feedback_max;
+float **feedback_instant;
+
+/* Gentlemen, power up the Variance hammer */
+float **floor_y;
+float **floor_yy;
+int floor_count;
+
+float **ph_acc;
+float **ph_max;
+float **ph_instant;
+
+float **xmappingL;
+float **xmappingH;
+int metascale = -1;
+int metawidth = -1;
+int metares = -1;
+int metanoise = 0;
+
+sig_atomic_t acc_clear=0;
+sig_atomic_t acc_rewind=0;
+sig_atomic_t acc_loop=0;
+
+sig_atomic_t process_active=0;
+sig_atomic_t process_exit=0;
+
+static void init_process(void){
+ int i;
+ if(window==NULL){
+ process_work=calloc(blocksize+2,sizeof(*process_work));
+ feedback_count=calloc(total_ch,sizeof(*feedback_count));
+ plot_data=calloc(total_ch,sizeof(*plot_data));
+
+ feedback_acc=malloc(total_ch*sizeof(*feedback_acc));
+ feedback_max=malloc(total_ch*sizeof(*feedback_max));
+ feedback_instant=malloc(total_ch*sizeof(*feedback_instant));
+ floor_y=malloc(total_ch*sizeof(*floor_y));
+ floor_yy=malloc(total_ch*sizeof(*floor_yy));
+
+ ph_acc=malloc(total_ch*sizeof(*ph_acc));
+ ph_max=malloc(total_ch*sizeof(*ph_max));
+ ph_instant=malloc(total_ch*sizeof(*ph_instant));
+
+ freqbuffer=fftwf_malloc((blocksize+2)*sizeof(*freqbuffer));
+ for(i=0;i<total_ch;i++){
+
+ floor_y[i]=calloc(blocksize/2+1,sizeof(**floor_y));
+ floor_yy[i]=calloc(blocksize/2+1,sizeof(**floor_yy));
+ feedback_acc[i]=calloc(blocksize/2+1,sizeof(**feedback_acc));
+ feedback_max[i]=calloc(blocksize/2+1,sizeof(**feedback_max));
+ feedback_instant[i]=calloc(blocksize/2+1,sizeof(**feedback_instant));
+
+ ph_acc[i]=calloc(blocksize+2,sizeof(**ph_acc));
+ ph_max[i]=calloc(blocksize+2,sizeof(**ph_max));
+ ph_instant[i]=calloc(blocksize+2,sizeof(**ph_instant));
+ }
+
+ plan=fftwf_plan_dft_r2c_1d(blocksize,freqbuffer,
+ (fftwf_complex *)freqbuffer,
+ FFTW_ESTIMATE);
+
+ /* construct proper window (sin^4 I'd think) */
+ window = calloc(blocksize,sizeof(*window));
+ for(i=0;i<blocksize;i++)window[i]=sin(M_PIl*i/blocksize);
+ for(i=0;i<blocksize;i++)window[i]*=window[i];
+ for(i=0;i<blocksize;i++)window[i]=sin(window[i]*M_PIl*.5);
+ for(i=0;i<blocksize;i++)window[i]*=window[i]/(blocksize/4)*.778;
+ }
+}
+
+void rundata_clear(){
+ int i,j;
+ for(i=0;i<total_ch;i++){
+ feedback_count[i]=0;
+ memset(feedback_acc[i],0,(blocksize/2+1)*sizeof(**feedback_acc));
+ memset(feedback_max[i],0,(blocksize/2+1)*sizeof(**feedback_max));
+ memset(feedback_instant[i],0,(blocksize/2+1)*sizeof(**feedback_instant));
+
+ for(j=0;j<blocksize+2;j++){
+ ph_acc[i][j]=0;
+ ph_max[i][j]=0;
+ ph_instant[i][j]=0;
+ }
+ }
+ acc_clear=0;
+}
+
+extern int plot_noise;
+
+/* return 0 on EOF, 1 otherwise */
+static int process(){
+ int fi,i,j,ch;
+ int eof_all;
+ int noise=plot_noise;
+
+ if(acc_rewind)
+ rewind_files();
+ acc_rewind=0;
+
+ if(input_read(acc_loop,0))
+ return 0;
+
+ if(acc_clear)
+ rundata_clear();
+
+ /* by channel */
+ ch=0;
+ for(fi=0;fi<inputs;fi++){
+ if(blockbufferfill[fi]){
+ for(i=ch;i<ch+channels[fi];i++){
+
+ float *data=blockbuffer[i];
+
+ /* window the blockbuffer into the FFT buffer */
+ for(j=0;j<blocksize;j++){
+ freqbuffer[j]=data[j]*window[j];
+ }
+
+ /* transform */
+ fftwf_execute(plan);
+
+ pthread_mutex_lock(&feedback_mutex);
+
+ /* perform desired accumulations */
+ for(j=0;j<blocksize+2;j+=2){
+ float R = freqbuffer[j];
+ float I = freqbuffer[j+1];
+ float sqR = R*R;
+ float sqI = I*I;
+ float sqM = sqR+sqI;
+
+ if(noise==1){
+ floor_yy[i][j>>1]+=sqM*sqM;
+ floor_y[i][j>>1]+=sqM;
+ }
+
+ /* deal with phase accumulate/rotate */
+ if(i==ch){
+ /* normalize/store ref for later rotation */
+ process_work[j] = R;
+ process_work[j+1] = -I;
+
+ }else{
+ /* rotate signed square phase according to ref for phase calculation */
+ float pR;
+ float pI;
+ float rR = process_work[j];
+ float rI = process_work[j+1];
+ pR = (rR*R - rI*I);
+ pI = (rR*I + rI*R);
+
+ ph_instant[i][j]=pR;
+ ph_instant[i][j+1]=pI;
+
+ ph_acc[i][j]+=pR;
+ ph_acc[i][j+1]+=pI;
+
+ if(feedback_max[i][j>>1]<sqM){
+ ph_max[i][j]=pR;
+ ph_max[i][j+1]=pI;
+ }
+ }
+
+ feedback_instant[i][j>>1]=sqM;
+ feedback_acc[i][j>>1]+=sqM;
+
+ if(feedback_max[i][j>>1]<sqM)
+ feedback_max[i][j>>1]=sqM;
+
+ }
+ feedback_count[i]++;
+
+ pthread_mutex_unlock(&feedback_mutex);
+ }
+ }
+ ch+=channels[fi];
+ }
+ if(noise==1)
+ floor_count++;
+ feedback_increment++;
+ write(eventpipe[1],"",1);
+ return 1;
+}
+
+void *process_thread(void *dummy){
+ while(!process_exit && process());
+ process_active=0;
+ write(eventpipe[1],"",1);
+ return NULL;
+}
+
+void process_dump(int mode){
+ int fi,i,j,ch;
+ FILE *out;
+
+ {
+ out=fopen("accumulate.m","w");
+ ch = 0;
+ for(fi=0;fi<inputs;fi++){
+ for(i=0;i<blocksize/2+1;i++){
+ fprintf(out,"%f ",(double)i*rate[fi]/blocksize);
+
+ for(j=ch;j<ch+channels[fi];j++)
+ fprintf(out,"%f ",todB(feedback_acc[j][i])*.5);
+ fprintf(out,"\n");
+ }
+ fprintf(out,"\n");
+ ch+=channels[fi];
+ }
+ fclose(out);
+ }
+
+ {
+ out=fopen("max.m","w");
+ ch = 0;
+ for(fi=0;fi<inputs;fi++){
+ for(i=0;i<blocksize/2+1;i++){
+ fprintf(out,"%f ",(double)i*rate[fi]/blocksize);
+
+ for(j=ch;j<ch+channels[fi];j++)
+ fprintf(out,"%f ",todB(feedback_max[j][i])*.5);
+ fprintf(out,"\n");
+ }
+ fprintf(out,"\n");
+ ch+=channels[fi];
+ }
+ fclose(out);
+ }
+
+ {
+ out=fopen("instant.m","w");
+ ch = 0;
+ for(fi=0;fi<inputs;fi++){
+ for(i=0;i<blocksize/2+1;i++){
+ fprintf(out,"%f ",(double)i*rate[fi]/blocksize);
+
+ for(j=ch;j<ch+channels[fi];j++)
+ fprintf(out,"%f ",todB(feedback_instant[j][i])*.5);
+ fprintf(out,"\n");
+ }
+ fprintf(out,"\n");
+ ch+=channels[fi];
+ }
+ fclose(out);
+ }
+
+ {
+ out=fopen("accphase.m","w");
+ ch = 0;
+ for(fi=0;fi<inputs;fi++){
+
+ /* phase */
+ for(i=0;i<blocksize+2;i+=2){
+ fprintf(out,"%f ",(double)i*.5*rate[fi]/blocksize);
+ fprintf(out,"%f ",atan2(ph_acc[ch+1][i+1],ph_acc[ch+1][i])*57.29);
+ fprintf(out,"\n");
+ }
+ fprintf(out,"\n");
+ ch+=channels[fi];
+ }
+ fclose(out);
+ }
+
+}
+
+void clear_noise_floor(){
+ int i;
+ for(i=0;i<total_ch;i++){
+ memset(floor_y[i],0,(blocksize/2+1)*sizeof(**floor_y));
+ memset(floor_yy[i],0,(blocksize/2+1)*sizeof(**floor_yy));
+ }
+ floor_count=0;
+}
+
+/* how many bins to 'trim' off the edge of calculated data when we
+ know we've hit a boundary of marginal measurement */
+#define binspan 5
+
+float **process_fetch(int res, int scale, int mode, int link,
+ int *active, int width,
+ float *ymax, float *pmax, float *pmin,
+ float **yfloor,int noise){
+ int ch,ci,i,j,fi;
+ float **data;
+ float **ph;
+ float maxrate=-1.;
+ float nyq;
+
+ init_process();
+
+ for(fi=0;fi<inputs;fi++)
+ if(rate[fi]>maxrate)maxrate=rate[fi];
+ nyq=maxrate/2.;
+
+ *yfloor=NULL;
+
+ /* are our scale mappings up to date? */
+ if(res != metares || scale != metascale || width != metawidth){
+ if(!xmappingL) xmappingL = calloc(inputs, sizeof(*xmappingL));
+ if(!xmappingH) xmappingH = calloc(inputs, sizeof(*xmappingH));
+ metanoise=-1;
+
+ if(!work_floor)
+ work_floor = calloc(total_ch,sizeof(*work_floor));
+ for(i=0;i<total_ch;i++){
+ if(work_floor[i])
+ work_floor[i] = realloc(work_floor[i],(width+1)*sizeof(**work_floor));
+ else
+ work_floor[i] = calloc((width+1),sizeof(**work_floor));
+ }
+
+ for(fi=0;fi<inputs;fi++){
+
+ /* if mapping preexists, resize it */
+ if(xmappingL[fi]){
+ xmappingL[fi] = realloc(xmappingL[fi],(width+1)*sizeof(**xmappingL));
+ }else{
+ xmappingL[fi] = malloc((width+1)*sizeof(**xmappingL));
+ }
+ if(xmappingH[fi]){
+ xmappingH[fi] = realloc(xmappingH[fi],(width+1)*sizeof(**xmappingH));
+ }else{
+ xmappingH[fi] = malloc((width+1)*sizeof(**xmappingH));
+ }
+
+ metascale = scale;
+ metawidth = width;
+ metares = res;
+
+
+ /* generate new numbers */
+ for(i=0;i<width;i++){
+ float off=0;
+ float loff=1.;
+ float hoff=1.;
+ float lfreq,hfreq;
+
+ switch(res){
+ case 0: /* screen-resolution */
+ off=1.;
+ break;
+ case 1: /* 1/24th octave */
+ loff = .95918945710913818816;
+ hoff = 1.04254690518999138632;
+ break;
+ case 2: /* 1/12th octave */
+ loff = .94387431268169349664;
+ hoff = 1.05946309435929526455;
+ break;
+ case 3: /* 1/3th octave */
+ loff = .79370052598409973738;
+ hoff = 1.25992104989487316475;
+ break;
+ }
+
+ switch(scale){
+ case 0: /* log */
+ lfreq= pow(10.,(i-off)/(width-1)
+ * (log10(nyq)-log10(5.))
+ + log10(5.)) * loff;
+ hfreq= pow(10.,(i+off)/(width-1)
+ * (log10(nyq)-log10(5.))
+ + log10(5.)) * hoff;
+ break;
+ case 1: /* ISO */
+ lfreq= pow(2.,(i-off)/(width-1)
+ * (log2(nyq)-log2(25.))
+ + log2(25.)) * loff;
+ hfreq= pow(2.,(i+off)/(width-1)
+ * (log2(nyq)-log2(25.))
+ + log2(25.)) *hoff;
+ break;
+ case 2: /* screen-resolution linear */
+ lfreq=(i-off)*nyq/(width-1)*loff;
+ hfreq=(i+off)*nyq/(width-1)*hoff;
+ break;
+ }
+
+ xmappingL[fi][i]=lfreq/(rate[fi]*.5)*(blocksize/2);
+ xmappingH[fi][i]=hfreq/(rate[fi]*.5)*(blocksize/2);
+
+ }
+
+ for(i=0;i<width;i++){
+ if(xmappingL[fi][i]<0.)xmappingL[fi][i]=0.;
+ if(xmappingL[fi][i]>blocksize/2.)xmappingL[fi][i]=blocksize/2.;
+ if(xmappingH[fi][i]<0.)xmappingH[fi][i]=0.;
+ if(xmappingH[fi][i]>blocksize/2.)xmappingH[fi][i]=blocksize/2.;
+ }
+ }
+
+ for(i=0;i<total_ch;i++)
+ if(plot_data[i]){
+ plot_data[i] = realloc(plot_data[i],(width+1)*sizeof(**plot_data));
+ }else{
+ plot_data[i] = malloc((width+1)*sizeof(**plot_data));
+ }
+ }
+
+ /* 'illustrate' the noise floor */
+ if(noise){
+ if(plot_floor)
+ plot_floor=realloc(plot_floor,(width+1)*sizeof(*plot_floor));
+ else
+ plot_floor=calloc((width+1),sizeof(*plot_floor));
+
+ if(metanoise!=link){
+ float *y = plot_floor;
+ int ch=0;
+ metanoise=link;
+ for(i=0;i<width;i++)
+ y[i]=-300;
+
+ for(fi=0;fi<inputs;fi++){
+ float *L = xmappingL[fi];
+ float *H = xmappingH[fi];
+ float d = 1./floor_count;
+
+ for(ci=0;ci<channels[fi];ci++){
+ float *fy = floor_y[ci+ch];
+ float *fyy = floor_yy[ci+ch];
+ float *w = work_floor[ci+ch];
+
+ for(i=0;i<width;i++){
+ int first=floor(L[i]);
+ int last=floor(H[i]);
+ float esum;
+ float vsum;
+ float v = fyy[first]*floor_count - fy[first]*fy[first];
+
+ if(first==last){
+ float del=H[i]-L[i];
+ esum=fy[first]*del;
+ vsum=v*del;
+ }else{
+ float del=1.-(L[i]-first);
+ esum=fy[first]*del;
+ vsum=v*del;
+
+ for(j=first+1;j<last;j++){
+ v = fyy[j]*floor_count - fy[j]*fy[j];
+ esum+=fy[j];
+ vsum+=v;
+ }
+
+ v = fyy[last]*floor_count - fy[last]*fy[last];
+ del=(H[i]-last);
+ esum+=fy[last]*del;
+ vsum+=v*del;
+ }
+ vsum = 10*sqrt(vsum)*d;
+ esum*=d;
+ w[i] = esum+vsum*10;
+ esum = todB_a(w+i)*.5;
+
+ if(esum>y[i])y[i]=esum;
+ }
+ }
+ ch+=channels[fi];
+ }
+ }
+ if(link == LINK_INDEPENDENT && mode==0)
+ *yfloor=plot_floor;
+ }else{
+ for(i=0;i<total_ch;i++)
+ memset(work_floor[i],0,width*sizeof(**work_floor));
+ metanoise=-1;
+ }
+
+ /* mode selects the base data set */
+ switch(mode){
+ case 0: /* independent / instant */
+ data=feedback_instant;
+ ph=ph_instant;
+ break;
+ case 1: /* independent / max */
+ data=feedback_max;
+ ph=ph_max;
+ break;
+ case 2:
+ data=feedback_acc;
+ ph=ph_acc;
+ break;
+ }
+
+ ch=0;
+ *ymax = -150.;
+ *pmax = -180.;
+ *pmin = 180.;
+ for(fi=0;fi<inputs;fi++){
+ float *L = xmappingL[fi];
+ float *H = xmappingH[fi];
+
+ switch(link){
+ case LINK_INDEPENDENT:
+
+ for(ci=0;ci<channels[fi];ci++){
+ float *y = plot_data[ci+ch];
+ float *m = data[ci+ch];
+ if(active[ch+ci]){
+ for(i=0;i<width;i++){
+ int first=floor(L[i]);
+ int last=floor(H[i]);
+ float sum;
+
+ if(first==last){
+ float del=H[i]-L[i];
+ sum=m[first]*del;
+ }else{
+ float del=1.-(L[i]-first);
+ sum=m[first]*del;
+
+ for(j=first+1;j<last;j++)
+ sum+=m[j];
+
+ del=(H[i]-last);
+ sum+=m[last]*del;
+ }
+
+ sum=todB_a(&sum)*.5;
+ if(sum>*ymax)*ymax=sum;
+ y[i]=sum;
+ }
+ }
+ }
+ break;
+
+ case LINK_SUMMED:
+ {
+ float *y = plot_data[ch];
+ memset(y,0,(width+1)*sizeof(*y));
+
+ for(ci=0;ci<channels[fi];ci++){
+ float *m = data[ci+ch];
+ if(active[ch+ci]){
+ for(i=0;i<width;i++){
+ int first=floor(L[i]);
+ int last=floor(H[i]);
+
+ if(first==last){
+ float del=H[i]-L[i];
+ y[i]+=m[first]*del;
+ }else{
+ float del=1.-(L[i]-first);
+ y[i]+=m[first]*del;
+
+ for(j=first+1;j<last;j++)
+ y[i]+=m[j];
+
+ del=(H[i]-last);
+ y[i]+=m[last]*del;
+ }
+ }
+ }
+ }
+
+ for(i=0;i<width;i++){
+ float sum=todB_a(y+i)*.5;
+ if(sum>*ymax)*ymax=sum;
+ y[i]=sum;
+ }
+ }
+ break;
+
+ case LINK_SUB_FROM:
+ {
+ float *y = plot_data[ch];
+ if(active[ch]==0){
+ for(i=0;i<width;i++)
+ y[i]=-300;
+ }else{
+ for(ci=0;ci<channels[fi];ci++){
+ float *m = data[ci+ch];
+ if(ci==0 || active[ch+ci]){
+ for(i=0;i<width;i++){
+ int first=floor(L[i]);
+ int last=floor(H[i]);
+ float sum;
+
+ if(first==last){
+ float del=H[i]-L[i];
+ sum=m[first]*del;
+ }else{
+ float del=1.-(L[i]-first);
+ sum=m[first]*del;
+
+ for(j=first+1;j<last;j++)
+ sum+=m[j];
+
+ del=(H[i]-last);
+ sum+=m[last]*del;
+ }
+
+ if(ci==0){
+ y[i]=sum;
+ }else{
+ y[i]-=sum;
+ }
+ }
+ }
+ }
+
+ for(i=0;i<width;i++){
+ float v = (y[i]>0?y[i]:0);
+ float sum=todB_a(&v)*.5;
+ if(sum>*ymax)*ymax=sum;
+ y[i]=sum;
+ }
+ }
+ }
+ break;
+ case LINK_SUB_REF:
+ {
+ float *r = plot_data[ch];
+ for(ci=0;ci<channels[fi];ci++){
+ float *y = plot_data[ch+ci];
+ float *m = data[ci+ch];
+ if(ci==0 || active[ch+ci]){
+ for(i=0;i<width;i++){
+ int first=floor(L[i]);
+ int last=floor(H[i]);
+ float sum;
+
+ if(first==last){
+ float del=H[i]-L[i];
+ sum=m[first]*del;
+ }else{
+ float del=1.-(L[i]-first);
+ sum=m[first]*del;
+
+ for(j=first+1;j<last;j++)
+ sum+=m[j];
+
+ del=(H[i]-last);
+ sum+=m[last]*del;
+ }
+
+ if(ci==0){
+ r[i]=sum;
+ }else{
+ sum=(r[i]>sum?0.f:sum-r[i]);
+ y[i]=todB_a(&sum)*.5;
+ if(y[i]>*ymax)*ymax=y[i];
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case LINK_IMPEDENCE_p1:
+ case LINK_IMPEDENCE_1:
+ case LINK_IMPEDENCE_10:
+ {
+ float shunt = (link == LINK_IMPEDENCE_p1?.1:(link == LINK_IMPEDENCE_1?1:10));
+ float *r = plot_data[ch];
+
+ for(ci=0;ci<channels[fi];ci++){
+ float *y = plot_data[ci+ch];
+ float *m = data[ch+ci];
+
+ if(ci==0 || active[ch+ci]){
+ for(i=0;i<width;i++){
+ int first=floor(L[i]);
+ int last=floor(H[i]);
+ float sum;
+
+ if(first==last){
+ float del=H[i]-L[i];
+ sum=m[first]*del;
+ }else{
+ float del=1.-(L[i]-first);
+ sum=m[first]*del;
+
+ for(j=first+1;j<last;j++)
+ sum+=m[j];
+
+ del=(H[i]-last);
+ sum+=m[last]*del;
+ }
+
+ if(ci==0){
+ /* stash the reference in the work vector */
+ r[i]=sum;
+ }else{
+ /* the shunt */
+ /* 'r' collected at source, 'sum' across the shunt */
+ float V=sqrt(r[i]);
+ float S=sqrt(sum);
+
+ if(S>(1e-5) && V>S){
+ y[i] = shunt*(V-S)/S;
+ }else{
+ y[i] = NAN;
+ }
+ }
+ }
+ }
+ }
+
+ /* scan the resulting buffers for marginal data that would
+ produce spurious output. Specifically we look for sharp
+ falloffs of > 40dB or an original test magnitude under
+ -70dB. */
+ {
+ float max = -140;
+ for(i=0;i<width;i++){
+ float v = r[i] = todB_a(r+i)*.5;
+ if(v>max)max=v;
+ }
+
+ for(ci=1;ci<channels[fi];ci++){
+ if(active[ch+ci]){
+ float *y = plot_data[ci+ch];
+ for(i=0;i<width;i++){
+ if(r[i]<max-40 || r[i]<-70){
+ int j=i-binspan;
+ if(j<0)j=0;
+ for(;j<i;j++)
+ y[j]=NAN;
+ for(;j<width;j++){
+ if(r[j]>max-40 && r[j]>-70)break;
+ y[j]=NAN;
+ }
+ i=j+3;
+ for(;j<i && j<width;j++){
+ y[j]=NAN;
+ }
+ }
+ if(!isnan(y[i]) && y[i]>*ymax)*ymax = y[i];
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case LINK_PHASE: /* response/phase */
+
+ if(channels[fi]>=2){
+ float *om = plot_data[ch];
+ float *op = plot_data[ch+1];
+
+ float *r = data[ch];
+ float *rn = work_floor[ch];
+ float *m = data[ch+1];
+ float *mn = work_floor[ch+1];
+ float *p = ph[ch+1];
+ float mag[width];
+
+ if(feedback_count[ch]==0){
+ memset(om,0,width*sizeof(*om));
+ memset(op,0,width*sizeof(*op));
+ }else{
+ /* two vectors only; response and phase */
+ /* response */
+ if(active[ch] || active[ch+1]){
+ for(i=0;i<width;i++){
+ int first=floor(L[i]);
+ int last=floor(H[i]);
+ float sumR,sumM;
+
+ if(first==last){
+ float del=H[i]-L[i];
+ sumR=r[first]*del;
+ sumM=m[first]*del;
+ }else{
+ float del=1.-(L[i]-first);
+ sumR=r[first]*del;
+ sumM=m[first]*del;
+
+ for(j=first+1;j<last;j++){
+ sumR+=r[j];
+ sumM+=m[j];
+ }
+
+ del=(H[i]-last);
+ sumR+=r[last]*del;
+ sumM+=m[last]*del;
+ }
+
+ if(sumR>rn[i] && sumM>mn[i]){
+ mag[i] = todB_a(&sumR)*.5;
+ sumM /= sumR;
+ om[i] = todB_a(&sumM)*.5;
+ }else{
+ om[i] = NAN;
+ }
+ }
+ }
+
+ /* phase */
+ if(active[ch+1]){
+ for(i=0;i<width;i++){
+ int first=floor(L[i]);
+ int last=floor(H[i]);
+ float sumR,sumI;
+
+ if(first==last){
+ float del=H[i]-L[i];
+ sumR=p[(first<<1)]*del;
+ sumI=p[(first<<1)+1]*del;
+ }else{
+ float del=1.-(L[i]-first);
+ sumR=p[(first<<1)]*del;
+ sumI=p[(first<<1)+1]*del;
+
+ for(j=first+1;j<last;j++){
+ sumR+=p[(j<<1)];
+ sumI+=p[(j<<1)+1];
+ }
+
+ del=(H[i]-last);
+ sumR+=p[(last<<1)]*del;
+ sumI+=p[(last<<1)+1]*del;
+ }
+
+ if(!isnan(om[i])){
+ op[i] = atan2(sumI,sumR)*57.29;
+ }else{
+ op[i]=NAN;
+ }
+ }
+ }
+
+ /* scan the resulting buffers for marginal data that would
+ produce spurious output. Specifically we look for sharp
+ falloffs of > 40dB or an original test magnitude under
+ -70dB. */
+ if(active[ch] || active[ch+1]){
+ for(i=0;i<width;i++){
+ if(isnan(om[i])){
+ int j=i-binspan;
+ if(j<0)j=0;
+ for(;j<i;j++){
+ om[j]=NAN;
+ op[j]=NAN;
+ }
+ for(;j<width;j++){
+ if(!isnan(om[j]))break;
+ om[j]=NAN;
+ op[j]=NAN;
+ }
+ i=j+3;
+ for(;j<i && j<width;j++){
+ om[j]=NAN;
+ op[j]=NAN;
+ }
+ }
+ if(om[i]>*ymax)*ymax = om[i];
+ if(op[i]>*pmax)*pmax = op[i];
+ if(op[i]<*pmin)*pmin = op[i];
+
+ }
+ }
+ }
+ }
+ break;
+
+ case LINK_THD: /* THD */
+ case LINK_THD2: /* THD-2 */
+ case LINK_THDN: /* THD+N */
+ case LINK_THDN2: /* THD+N-2 */
+
+
+ break;
+
+ }
+ ch+=channels[fi];
+ }
+
+ return plot_data;
+}
+
Copied: trunk/spectrum/spectrum.c (from rev 18228, trunk/spectrum/main.c)
===================================================================
--- trunk/spectrum/spectrum.c (rev 0)
+++ trunk/spectrum/spectrum.c 2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,296 @@
+/*
+ *
+ * gtk2 spectrum analyzer
+ *
+ * Copyright (C) 2004 Monty
+ *
+ * This analyzer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The analyzer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Postfish; see the file COPYING. If not, write to the
+ * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#include "analyzer.h"
+#include "io.h"
+#include <signal.h>
+#include <getopt.h>
+#include <fenv.h> // Thank you C99!
+#include <fftw3.h>
+#include <gtk/gtk.h>
+#include "version.h"
+
+int eventpipe[2];
+char *version;
+char *inputname[MAX_FILES];
+int inputs=0;
+int blocksize = 131072;
+
+void handler(int sig){
+ signal(sig,SIG_IGN);
+ if(sig!=SIGINT){
+ fprintf(stderr,
+ "\nTrapped signal %d; exiting!\n"
+ "This signal almost certainly indicates a bug in the analyzer;\n"
+ "If this version of the analyzer is newer than a few months old,\n"
+ "please email a detailed report of the crash along with\n"
+ "processor type, OS version, FFTW3 version, and as much\n"
+ "information as possible about what caused the crash. The best\n"
+ "possible report will outline the exact steps needed to\n"
+ "reproduce the error, ensuring that I can fix the bug as\n"
+ "quickly as possible.\n\n"
+ "-- monty at xiph.org, spectrum revision %s\n\n",sig,version);
+ }
+
+ gtk_main_quit();
+}
+
+const char *optstring = "-r:c:EeBlb:suhF:";
+
+struct option options [] = {
+ {"rate",required_argument,NULL,'r'},
+ {"channels",required_argument,NULL,'c'},
+ {"big-endian",no_argument,NULL,'E'},
+ {"little-endian",no_argument,NULL,'e'},
+ {"bits",required_argument,NULL,'b'},
+ {"signed",no_argument,NULL,'s'},
+ {"unsigned",no_argument,NULL,'u'},
+ {"help",no_argument,NULL,'h'},
+ {"fft-size",required_argument,NULL,'F'},
+
+ {NULL,0,NULL,0}
+};
+
+static void usage(FILE *f){
+ fprintf( f,
+"\ngtk2 spectrum analyzer, revision %s\n\n"
+
+"USAGE:\n\n"
+" spectrum [options] [file]\n\n"
+
+"OPTIONS:\n\n"
+" -b --bits <bits> : Force input to be read as 8, 16, 24 or 32 bit\n"
+" PCM. Default bit depth is normally read from\n"
+" the file/stream header or set to 16 bits\n"
+" for raw input.\n"
+" -B -E --big-endian : Force input to be read as big endian.\n"
+" Default endianness is normally read from the\n"
+" file/stream header or set to host"
+" endianness for raw input.\n"
+" -c --channels <channels> : Input channel number override; use to\n"
+" specify the number of channels in a raw\n"
+" input. default: 1\n"
+" -e -l --little-endian : Force input to be read as little endian.\n"
+" Default endianness is normally read from the\n"
+" file/stream header or set to host"
+" endianness for raw input.\n"
+" -F --fft-size : Set the size of the fft transform used. Valid\n"
+" range 8192 to 262144, 131072 default.\n"
+" -h --help : print this help\n"
+" -r --rate <Hz> : Input sample rate override in Hz; use to\n"
+" specify the rate of a raw input.\n"
+" default: 44100\n"
+" -s --signed : Force input to be read as signed PCM.\n"
+" Signedness is normally read from the \n"
+" file/stream header or set to signed for raw\n"
+" input.\n"
+" -u --unsigned : Force input to be read as unsigned PCM.\n"
+" Signedness is normally read from the \n"
+" file/stream header or set to signed for raw\n"
+" input.\n\n"
+
+"INPUT:\n\n"
+
+" Spectrum takes raw, WAV or AIFF input either from stdin or from \n"
+" file[s]/stream[s] specified on the command line.\n\n",version);
+
+}
+
+void parse_command_line(int argc, char **argv){
+ int c,long_option_index;
+
+ while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
+ switch(c){
+ case 1:
+ /* file name that belongs to current group */
+ if(inputs>=MAX_FILES){
+ fprintf(stderr,"Maximum of MAX_FILES input files exceeded. Oops. Programmer was lazy.\n\n");
+ exit(1);
+ }
+ inputname[inputs++]=strdup(optarg);
+ break;
+ case 'b':
+ /* force bit width */
+ {
+ int a=atoi(optarg);
+ bits[inputs]=a;
+ if(a!=8 && a!=16 && a!=24 && a!=32){
+ usage(stderr);
+ exit(1);
+ }
+ }
+ break;
+ case 'F':
+ blocksize = atoi(optarg);
+ if(blocksize<8192 || blocksize>262144){
+ usage(stderr);
+ exit(1);
+ }
+ break;
+ case 'B':case 'E':
+ /* force big endian */
+ bigendian[inputs]=1;
+ break;
+ case 'c':
+ /* force channels */
+ {
+ int a=atoi(optarg);
+ channels[inputs]=a;
+ if(a<1 || a>32){
+ usage(stderr);
+ exit(1);
+ }
+ }
+ break;
+ case 'l':case 'e':
+ /* force little endian */
+ bigendian[inputs]=0;
+ break;
+ case 'h':
+ usage(stdout);
+ exit(0);
+ case 'r':
+ /* force rate */
+ {
+ int a=atoi(optarg);
+ rate[inputs]=a;
+ if(a<4000 || a>200000){
+ usage(stderr);
+ exit(1);
+ }
+ }
+ break;
+ case 's':
+ /* force signed */
+ signedp[inputs]=1;
+ break;
+ case 'u':
+ /* force unsigned */
+ signedp[inputs]=0;
+ break;
+ default:
+ usage(stderr);
+ exit(0);
+ }
+ }
+}
+
+static int sigill=0;
+void sigill_handler(int sig){
+ /* make sure */
+ if(sig==SIGILL)sigill=1;
+}
+
+int main(int argc, char **argv){
+ int fi;
+
+ version=strstr(VERSION,"version.h");
+ if(version){
+ char *versionend=strchr(version,' ');
+ if(versionend)versionend=strchr(versionend+1,' ');
+ if(versionend)versionend=strchr(versionend+1,' ');
+ if(versionend)versionend=strchr(versionend+1,' ');
+ if(versionend){
+ int len=versionend-version-9;
+ version=strdup(version+10);
+ version[len-1]=0;
+ }
+ }else{
+ version="";
+ }
+
+ /* parse command line and open all the input files */
+ parse_command_line(argc, argv);
+
+ /* We do not care about FPEs; rather, underflow is nominal case, and
+ its better to ignore other traps in production than to crash the
+ app. Please inform the FPU of this. */
+
+#ifndef DEBUG
+ fedisableexcept(FE_INVALID);
+ fedisableexcept(FE_INEXACT);
+ fedisableexcept(FE_UNDERFLOW);
+ fedisableexcept(FE_OVERFLOW);
+#else
+ feenableexcept(FE_INVALID);
+ feenableexcept(FE_INEXACT);
+ feenableexcept(FE_UNDERFLOW);
+ feenableexcept(FE_OVERFLOW);
+#endif
+
+ /* Linux Altivec support has a very annoying problem; by default,
+ math on denormalized floats will simply crash the program. FFTW3
+ uses Altivec, so boom, but only random booms.
+
+ By the C99 spec, the above exception configuration is also
+ supposed to handle Altivec config, but doesn't. So we use the
+ below ugliness to both handle altivec and non-alitvec PPC. */
+
+#ifdef __PPC
+#include <altivec.h>
+ signal(SIGILL,sigill_handler);
+
+#if (defined __GNUC__) && (__GNUC__ == 3) && ! (defined __APPLE_CC__)
+ __vector unsigned short noTrap =
+ (__vector unsigned short){0,0,0,0,0,0,0x1,0};
+#else
+ vector unsigned short noTrap =
+ (vector unsigned short)(0,0,0,0,0,0,0x1,0);
+#endif
+
+ vec_mtvscr(noTrap);
+#endif
+
+ /* easiest way to inform gtk of changes and not deal with locking
+ issues around the UI */
+ if(pipe(eventpipe)){
+ fprintf(stderr,"Unable to open event pipe:\n"
+ " %s\n",strerror(errno));
+
+ exit(1);
+ }
+
+ /* Allows event compression on the read side */
+ if(fcntl(eventpipe[0], F_SETFL, O_NONBLOCK)){
+ fprintf(stderr,"Unable to set O_NONBLOCK on event pipe:\n"
+ " %s\n",strerror(errno));
+
+ exit(1);
+ }
+
+ //signal(SIGINT,handler);
+ signal(SIGSEGV,handler);
+
+ if(input_load())exit(1);
+
+ /* select the full-block slice size: ~10fps */
+ for(fi=0;fi<inputs;fi++){
+ blockslice[fi]=rate[fi]/10;
+ while(blockslice[fi]>blocksize/2)blockslice[fi]/=2;
+ }
+
+ /* go */
+ panel_go(argc,argv);
+
+ return(0);
+}
Modified: trunk/spectrum/version.h
===================================================================
--- trunk/spectrum/version.h 2012-04-12 01:40:12 UTC (rev 18228)
+++ trunk/spectrum/version.h 2012-04-12 14:29:17 UTC (rev 18229)
@@ -1,2 +1,2 @@
#define VERSION "$Id$ "
-/* DO NOT EDIT: Automated versioning hack [Wed Apr 11 21:38:57 EDT 2012] */
+/* DO NOT EDIT: Automated versioning hack [Thu Apr 12 10:26:00 EDT 2012] */
Added: trunk/spectrum/wave_panel.c
===================================================================
--- trunk/spectrum/wave_panel.c (rev 0)
+++ trunk/spectrum/wave_panel.c 2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,758 @@
+/*
+ *
+ * gtk2 waveform viewer
+ *
+ * Copyright (C) 2004 Monty
+ *
+ * This analyzer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The analyzer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Postfish; see the file COPYING. If not, write to the
+ * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#include "waveform.h"
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include "fisharray.h"
+#include "io.h"
+#include "wave_plot.h"
+
+sig_atomic_t increment_fish=0;
+
+static struct panel {
+ GtkWidget *twirlimage;
+ GdkPixmap *ff[19];
+ GdkBitmap *fb[19];
+
+ GtkAccelGroup *group;
+ GtkWidget *toplevel;
+
+
+ guint fishframe_timer;
+ int fishframe_init;
+ int fishframe;
+
+ GtkWidget *plot;
+ GtkWidget *run;
+ GtkWidget **chbuttons;
+
+} p;
+
+int plot_range=0;
+int plot_scale=0;
+int plot_span=0;
+int plot_rchoice=0;
+int plot_schoice=0;
+int plot_spanchoice=0;
+int plot_interval=0;
+int plot_trigger=0;
+int plot_hold=0;
+int plot_last_update=0;
+int *active;
+
+int overslice[MAX_FILES]= {-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1};
+
+static void replot(struct panel *p){
+ /* update the waveform display; send new data */
+ if(!plot_hold){
+ pthread_mutex_lock(&feedback_mutex);
+ plot_refresh(PLOT(p->plot));
+ plot_last_update=feedback_increment;
+ pthread_mutex_unlock(&feedback_mutex);
+ }
+}
+
+static void shutdown(void){
+ gtk_main_quit();
+}
+
+/* gotta have the Fucking Fish */
+static int reanimate_fish(struct panel *p){
+ if(process_active || (p->fishframe>0 && p->fishframe<12)){
+ /* continue spinning */
+ if(increment_fish)p->fishframe++;
+ if(p->fishframe>=12)p->fishframe=0;
+
+ gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
+ p->ff[p->fishframe],
+ p->fb[p->fishframe]);
+
+ if(p->fishframe==0 && !process_active){
+ /* reschedule to blink */
+ p->fishframe_timer=
+ g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+ return FALSE;
+ }
+
+ }else{
+ p->fishframe++;
+ if(p->fishframe<=1)p->fishframe=12;
+ if(p->fishframe>=19)p->fishframe=0;
+
+ gtk_image_set_from_pixmap(GTK_IMAGE(p->twirlimage),
+ p->ff[p->fishframe],
+ p->fb[p->fishframe]);
+
+
+ if(p->fishframe==12){
+ /* reschedule to animate */
+ p->fishframe_timer=
+ g_timeout_add(10,(GSourceFunc)reanimate_fish,p);
+ return FALSE;
+ }
+ if(p->fishframe==0){
+ /* reschedule to blink */
+ p->fishframe_timer=
+ g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static void animate_fish(struct panel *p){
+ if(p->fishframe_init){
+ g_source_remove(p->fishframe_timer);
+ p->fishframe_timer=
+ g_timeout_add(80,(GSourceFunc)reanimate_fish,p);
+ }else{
+ p->fishframe_init=1;
+ p->fishframe_timer=
+ g_timeout_add(rand()%1000*30,(GSourceFunc)reanimate_fish,p);
+ }
+}
+
+static void set_fg(GtkWidget *c, gpointer in){
+ GdkColor *rgb = in;
+ gtk_widget_modify_fg(c,GTK_STATE_NORMAL,rgb);
+ gtk_widget_modify_fg(c,GTK_STATE_ACTIVE,rgb);
+ gtk_widget_modify_fg(c,GTK_STATE_PRELIGHT,rgb);
+ gtk_widget_modify_fg(c,GTK_STATE_SELECTED,rgb);
+
+ /* buttons usually have internal labels */
+ if(GTK_IS_CONTAINER(c))
+ gtk_container_forall (GTK_CONTAINER(c),set_fg,in);
+}
+
+static void rangechange(GtkWidget *widget,struct panel *p){
+ int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+ plot_rchoice=choice;
+ switch(choice){
+ case 0:
+ plot_range=0;
+ break;
+ case 1:
+ plot_range=-6;
+ break;
+ case 2:
+ plot_range=-14;
+ break;
+ case 3:
+ plot_range=-20;
+ break;
+ case 4:
+ plot_range=-40;
+ break;
+ case 5:
+ plot_range=-60;
+ break;
+ }
+ plot_setting(PLOT(p->plot),plot_range,plot_scale,plot_interval,plot_span,plot_rchoice,plot_schoice,plot_spanchoice,NULL,NULL);
+}
+
+static void scalechange(GtkWidget *widget,struct panel *p){
+ int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+ plot_schoice=choice;
+ switch(choice){
+ case 0:
+ plot_scale=0;
+ break;
+ case 1:
+ plot_scale=-65;
+ break;
+ case 2:
+ plot_scale=-96;
+ break;
+ case 3:
+ plot_scale=-120;
+ break;
+ case 4:
+ plot_scale=-160;
+ break;
+ }
+ plot_setting(PLOT(p->plot),plot_range,plot_scale,plot_interval,plot_span,plot_rchoice,plot_schoice,plot_spanchoice,NULL,NULL);
+}
+
+static void spanchange(GtkWidget *widget,struct panel *p){
+ int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+ plot_spanchoice=choice;
+ switch(choice){
+ case 0:
+ plot_span=1000000;
+ break;
+ case 1:
+ plot_span=500000;
+ break;
+ case 2:
+ plot_span=200000;
+ break;
+ case 3:
+ plot_span=100000;
+ break;
+ case 4:
+ plot_span=50000;
+ break;
+ case 5:
+ plot_span=20000;
+ break;
+ case 6:
+ plot_span=10000;
+ break;
+ case 7:
+ plot_span=5000;
+ break;
+ case 8:
+ plot_span=2000;
+ break;
+ case 9:
+ plot_span=1000;
+ break;
+ case 10:
+ plot_span=500;
+ break;
+ case 11:
+ plot_span=200;
+ break;
+ case 12:
+ plot_span=100;
+ break;
+ }
+
+ /* update interval limited to < 25fps */
+ int temp = (plot_interval < 50000 ? 50000:plot_interval),fi;
+
+ if(temp <= plot_span){
+ /* if the fps-limited update interval is shorter than or equal to
+ the span, we simply frame limit */
+ for(fi=0;fi<inputs;fi++){
+ blockslice[fi]=rate[fi]*temp/1000000;
+ overslice[fi]=rate[fi]*temp/1000000;
+ }
+ }else{
+ /* if the limited update interval is longer than the span, we
+ overdraw */
+ for(fi=0;fi<inputs;fi++)
+ blockslice[fi]=rate[fi]*temp/1000000;
+ for(fi=0;fi<inputs;fi++)
+ overslice[fi]=rate[fi]*plot_interval/1000000;
+ }
+ plot_setting(PLOT(p->plot),plot_range,plot_scale,plot_interval,plot_span,plot_rchoice,plot_schoice,plot_spanchoice,blockslice,overslice);
+}
+
+/* intervals that are >= the span and would result in > 25fps are overdrawn */
+static void intervalchange(GtkWidget *widget,struct panel *p){
+ int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+ switch(choice){
+ case 0:
+ plot_interval=1000000;
+ break;
+ case 1:
+ plot_interval=500000;
+ break;
+ case 2:
+ plot_interval=200000;
+ break;
+ case 3:
+ plot_interval=100000;
+ break;
+ case 4:
+ plot_interval=50000; // 20/sec
+ break;
+ case 5:
+ plot_interval=20000; // 50/sec
+ break;
+ case 6:
+ plot_interval=10000;
+ break;
+ case 7:
+ plot_interval=5000;
+ break;
+ case 8:
+ plot_interval=2000;
+ break;
+ case 9:
+ plot_interval=1000;
+ break;
+ case 10:
+ plot_interval=500;
+ break;
+ case 11:
+ plot_interval=200;
+ break;
+ case 12:
+ plot_interval=100;
+ break;
+ }
+
+ /* update interval limited to < 25fps */
+ int temp = (plot_interval < 50000 ? 50000:plot_interval),fi;
+
+ if(temp <= plot_span){
+ /* if the fps-limited update interval is shorter than or equal to
+ the span, we simply frame limit */
+ for(fi=0;fi<inputs;fi++){
+ blockslice[fi]=rate[fi]*temp/1000000;
+ overslice[fi]=rate[fi]*temp/1000000;
+ }
+ }else{
+ /* if the limited update interval is longer than the span, we
+ overdraw */
+ for(fi=0;fi<inputs;fi++)
+ blockslice[fi]=rate[fi]*temp/1000000;
+ for(fi=0;fi<inputs;fi++)
+ overslice[fi]=rate[fi]*plot_interval/1000000;
+ }
+ plot_setting(PLOT(p->plot),plot_range,plot_scale,plot_interval,plot_span,plot_rchoice,plot_schoice,plot_spanchoice,blockslice,overslice);
+}
+
+static void triggerchange(GtkWidget *widget,struct panel *p){
+ int choice=gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
+ /* nothing but free-run supported right now */
+}
+
+static void runchange(GtkWidget *widget,struct panel *p){
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))){
+ if(!process_active){
+ pthread_t thread_id;
+ process_active=1;
+ process_exit=0;
+ animate_fish(p);
+ pthread_create(&thread_id,NULL,&process_thread,NULL);
+ }
+ }else{
+ process_exit=1;
+ while(process_active)sched_yield();
+ }
+}
+
+static void holdchange(GtkWidget *widget,struct panel *p){
+ plot_hold=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+ replot(p);
+ plot_draw(PLOT(p->plot));
+}
+
+static void loopchange(GtkWidget *widget,struct panel *p){
+ acc_loop=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+}
+
+static void rewindchange(GtkWidget *widget,struct panel *p){
+ acc_rewind=1;
+}
+
+static void chlabels(GtkWidget *widget,struct panel *p){
+ /* scan state, update labels on channel buttons, set sensitivity
+ based on grouping and mode */
+ int fi,ch,i;
+ char buf[80];
+
+ for(i=0;i<total_ch;i++)
+ active[i]=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(p->chbuttons[i]));
+
+ plot_set_active(PLOT(p->plot),active);
+}
+
+extern char *version;
+void panel_create(struct panel *panel){
+ int i;
+
+ GtkWidget *topplace,*topal,*topalb;
+
+ GtkWidget *topframe=gtk_frame_new (NULL);
+ GtkWidget *toplabel=gtk_label_new (NULL);
+ GtkWidget *quitbutton=gtk_button_new_with_mnemonic("_quit");
+ GtkWidget *mainbox=gtk_hbox_new(0,6);
+ GdkWindow *root=gdk_get_default_root_window();
+ GtkWidget *rightbox=gtk_vbox_new(0,0);
+ GtkWidget *leftbox=gtk_vbox_new(0,6);
+
+ panel->toplevel=gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ panel->group = gtk_accel_group_new ();
+ gtk_window_add_accel_group (GTK_WINDOW(panel->toplevel), panel->group);
+
+ char versionmarkup[240];
+ snprintf(versionmarkup,240," <span size=\"large\" weight=\"bold\" "
+ "style=\"italic\" foreground=\"dark blue\">"
+ "Waveform Viewer</span> <span size=\"small\" foreground=\"#606060\">"
+ "revision %s</span> ",
+ version);
+
+ /* the Fucking Fish */
+ for(i=0;i<19;i++)
+ panel->ff[i]=gdk_pixmap_create_from_xpm_d(root,
+ panel->fb+i,NULL,ff_xpm[i]);
+ panel->twirlimage=gtk_image_new_from_pixmap(panel->ff[0],panel->fb[0]);
+
+ active = calloc(total_ch,sizeof(*active));
+
+ topplace=gtk_table_new(1,1,0);
+ topalb=gtk_hbox_new(0,0);
+ topal=gtk_alignment_new(1,0,0,0);
+
+ gtk_widget_set_name(quitbutton,"quitbutton");
+
+ gtk_box_pack_start(GTK_BOX(topalb),quitbutton,0,0,0);
+ gtk_container_add (GTK_CONTAINER(topal),topalb);
+
+ gtk_table_attach_defaults(GTK_TABLE(topplace),
+ topal,0,1,0,1);
+ gtk_table_attach_defaults(GTK_TABLE(topplace),
+ topframe,0,1,0,1);
+
+ gtk_container_add (GTK_CONTAINER (panel->toplevel), topplace);
+ gtk_container_set_border_width (GTK_CONTAINER (quitbutton), 3);
+
+ g_signal_connect (G_OBJECT (quitbutton), "clicked",
+ G_CALLBACK (shutdown), NULL);
+ gtk_widget_add_accelerator (quitbutton, "activate", panel->group, GDK_q, 0, 0);
+
+ gtk_container_set_border_width (GTK_CONTAINER (topframe), 3);
+ gtk_container_set_border_width (GTK_CONTAINER (mainbox), 3);
+ gtk_frame_set_shadow_type(GTK_FRAME(topframe),GTK_SHADOW_ETCHED_IN);
+ gtk_frame_set_label_widget(GTK_FRAME(topframe),toplabel);
+ gtk_label_set_markup(GTK_LABEL(toplabel),versionmarkup);
+
+ gtk_container_add (GTK_CONTAINER(topframe), mainbox);
+
+ g_signal_connect (G_OBJECT (panel->toplevel), "delete_event",
+ G_CALLBACK (shutdown), NULL);
+
+ /* add the waveform plot box */
+ panel->plot=plot_new(blocksize,inputs,channels,rate);
+ gtk_box_pack_end(GTK_BOX(leftbox),panel->plot,1,1,0);
+ gtk_box_pack_start(GTK_BOX(mainbox),leftbox,1,1,0);
+
+ /*fish */
+ {
+ GtkWidget *box=gtk_hbox_new(1,1);
+ GtkWidget *fishbox=gtk_hbox_new(0,0);
+ gtk_box_pack_end(GTK_BOX(fishbox),panel->twirlimage,0,0,0);
+ gtk_container_set_border_width (GTK_CONTAINER (fishbox), 3);
+
+ gtk_box_pack_start(GTK_BOX(box),fishbox,0,0,0);
+ gtk_box_pack_start(GTK_BOX(rightbox),box,0,0,0);
+ }
+
+ /* rate */
+ /* channels */
+ /* bits */
+ {
+ int fi;
+ int ch=0;
+ char buffer[160];
+ GtkWidget *label;
+ //GtkWidget *vbox=gtk_vbox_new(1,1);
+
+ GtkWidget *sep=gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(rightbox),sep,0,0,6);
+
+ panel->chbuttons = calloc(total_ch,sizeof(*panel->chbuttons));
+ for(fi=0;fi<inputs;fi++){
+
+ char *lastslash = strrchr(inputname[fi],'/');
+ sprintf(buffer,"%s",(lastslash?lastslash+1:inputname[fi]));
+ label=gtk_label_new(buffer);
+ gtk_widget_set_name(label,"readout");
+ gtk_box_pack_start(GTK_BOX(rightbox),label,0,0,0);
+
+ sprintf(buffer,"%dHz %dbit",rate[fi],bits[fi]);
+ label=gtk_label_new(buffer);
+ gtk_widget_set_name(label,"readout");
+ gtk_box_pack_start(GTK_BOX(rightbox),label,0,0,0);
+
+ for(i=ch;i<ch+channels[fi];i++){
+ GtkWidget *button=panel->chbuttons[i]=gtk_toggle_button_new();
+
+ sprintf(buffer,"channel %d", i-ch);
+ gtk_button_set_label(GTK_BUTTON(button),buffer);
+
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),1);
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (chlabels), panel);
+ gtk_box_pack_start(GTK_BOX(rightbox),button,0,0,0);
+ }
+
+ GtkWidget *sep=gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(rightbox),sep,0,0,6);
+
+ ch+=channels[fi];
+
+ }
+ chlabels(NULL,panel);
+ }
+
+ GtkWidget *bbox=gtk_vbox_new(0,0);
+
+ /* add the action buttons */
+ /* range */
+ {
+ GtkWidget *menu=gtk_combo_box_new_text();
+ char *entries[]={"\xC2\xB1""1.0 / 0dBFS",
+ "\xC2\xB1""0.5 / -6dBFS",
+ "\xC2\xB1""0.2 / -14dBFS",
+ "\xC2\xB1""0.1 / -20dBFS",
+ "\xC2\xB1""0.01 / -40dBFS",
+ "\xC2\xB1""0.001 / -60dBFS"};
+ for(i=0;i<6;i++)
+ gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
+ gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+ g_signal_connect (G_OBJECT (menu), "changed",
+ G_CALLBACK (rangechange), panel);
+ }
+
+ /* scale */
+ {
+ GtkWidget *menu=gtk_combo_box_new_text();
+ char *entries[]={"linear","-65dB+","-96dB+","-120dB+","-160dB+"};
+ for(i=0;i<5;i++)
+ gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
+ gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+ g_signal_connect (G_OBJECT (menu), "changed",
+ G_CALLBACK (scalechange), panel);
+ }
+
+ /* span */
+ {
+ GtkWidget *menu=gtk_combo_box_new_text();
+ char *entries[]={"1s span",
+ "500ms span","200ms span","100ms span",
+ "50ms span","20ms span","10ms span",
+ "5ms span","2ms span","1ms span",
+ "500\xCE\xBCs span","200\xCE\xBCs span",
+ "100\xCE\xBCs span"};
+ for(i=0;i<13;i++)
+ gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
+ gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+ g_signal_connect (G_OBJECT (menu), "changed",
+ G_CALLBACK (spanchange), panel);
+ }
+
+ /* trigger */
+ {
+ GtkWidget *menu=gtk_combo_box_new_text();
+ char *entries[]={"free run"};
+ for(i=0;i<1;i++)
+ gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(menu),0);
+ gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+ g_signal_connect (G_OBJECT (menu), "changed",
+ G_CALLBACK (triggerchange), panel);
+ }
+
+ /* interval */
+ {
+ GtkWidget *menu=gtk_combo_box_new_text();
+ char *entries[]={"1s interval",
+ "500ms interval","200ms interval","100ms interval",
+ "50ms interval","20ms interval","10ms interval",
+ "5ms interval","2ms interval","1ms interval",
+ "500\xCE\xBCs interval","200\xCE\xBCs interval",
+ "100\xCE\xBCsinterval "};
+ for(i=0;i<13;i++)
+ gtk_combo_box_append_text (GTK_COMBO_BOX (menu), entries[i]);
+ gtk_combo_box_set_active(GTK_COMBO_BOX(menu),3);
+ gtk_box_pack_start(GTK_BOX(bbox),menu,0,0,0);
+ g_signal_connect (G_OBJECT (menu), "changed",
+ G_CALLBACK (intervalchange), panel);
+ }
+
+ {
+ GtkWidget *sep=gtk_hseparator_new();
+ gtk_box_pack_start(GTK_BOX(bbox),sep,0,0,4);
+ }
+
+ /* run/pause */
+ {
+ GtkWidget *button=gtk_toggle_button_new_with_mnemonic("_run");
+ gtk_widget_add_accelerator (button, "activate", panel->group, GDK_space, 0, 0);
+ gtk_widget_add_accelerator (button, "activate", panel->group, GDK_r, 0, 0);
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (runchange), panel);
+ gtk_box_pack_start(GTK_BOX(bbox),button,0,0,0);
+ panel->run=button;
+ }
+
+ /* hold */
+ /* loop */
+ {
+ GtkWidget *box=gtk_hbox_new(1,1);
+ GtkWidget *button=gtk_toggle_button_new_with_mnemonic("_hold");
+ gtk_widget_add_accelerator (button, "activate", panel->group, GDK_h, 0, 0);
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (holdchange), panel);
+ gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
+
+ button=gtk_toggle_button_new_with_mnemonic("_loop");
+ gtk_widget_add_accelerator (button, "activate", panel->group, GDK_l, 0, 0);
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (loopchange), panel);
+ gtk_box_pack_start(GTK_BOX(box),button,1,1,0);
+ gtk_widget_set_sensitive(button,global_seekable);
+
+ gtk_box_pack_start(GTK_BOX(bbox),box,0,0,0);
+ }
+
+ /* clear */
+ /* rewind */
+ {
+ GtkWidget *button=gtk_button_new_with_mnemonic("re_wind");
+ gtk_widget_add_accelerator (button, "activate", panel->group, GDK_w, 0, 0);
+ g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (rewindchange), panel);
+ gtk_widget_set_sensitive(button,global_seekable);
+ gtk_box_pack_start(GTK_BOX(bbox),button,1,1,0);
+ }
+
+ gtk_box_pack_end(GTK_BOX(rightbox),bbox,0,0,0);
+ gtk_box_pack_start(GTK_BOX(mainbox),rightbox,0,0,0);
+
+ gtk_widget_show_all(panel->toplevel);
+ //gtk_window_set_resizable(GTK_WINDOW(panel->toplevel),0);
+
+}
+
+static gboolean async_event_handle(GIOChannel *channel,
+ GIOCondition condition,
+ gpointer data){
+ struct panel *panel=data;
+ char buf[1];
+
+ /* read all pending */
+ while(read(eventpipe[0],buf,1)>0);
+
+ increment_fish=1;
+
+ /* check playback status and update the run button if needed */
+ if(process_active && panel->run &&
+ !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel->run)))
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel->run),1);
+ if(!process_active && panel->run &&
+ gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(panel->run)))
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(panel->run),0);
+
+ /* update the waveform display; send new data */
+ pthread_mutex_lock(&feedback_mutex);
+ if(plot_last_update!=feedback_increment){
+ pthread_mutex_unlock(&feedback_mutex);
+ replot(panel);
+ plot_draw(PLOT(panel->plot));
+
+ while (gtk_events_pending())
+ gtk_main_iteration();
+ }else
+ pthread_mutex_unlock(&feedback_mutex);
+
+ return TRUE;
+}
+
+static int look_for_gtkrc(char *filename){
+ FILE *f=fopen(filename,"r");
+ if(!f)return 0;
+ fprintf(stderr,"Loading waveform-gtkrc file found at %s\n",filename);
+ gtk_rc_add_default_file(filename);
+ return 1;
+}
+
+void panel_go(int argc,char *argv[]){
+ char *homedir=getenv("HOME");
+ int found=0;
+ memset(&p,0,sizeof(p));
+
+ found|=look_for_gtkrc(ETCDIR"/waveform-gtkrc");
+ {
+ char *rcdir=getenv("HOME");
+ if(rcdir){
+ char *rcfile="/.spectrum/waveform-gtkrc";
+ char *homerc=calloc(1,strlen(rcdir)+strlen(rcfile)+1);
+ strcat(homerc,homedir);
+ strcat(homerc,rcfile);
+ found|=look_for_gtkrc(homerc);
+ }
+ }
+ {
+ char *rcdir=getenv("SPECTRUM_RCDIR");
+ if(rcdir){
+ char *rcfile="/waveform-gtkrc";
+ char *homerc=calloc(1,strlen(rcdir)+strlen(rcfile)+1);
+ strcat(homerc,homedir);
+ strcat(homerc,rcfile);
+ found|=look_for_gtkrc(homerc);
+ }
+ }
+ found|=look_for_gtkrc("./waveform-gtkrc");
+
+ if(!found){
+
+ fprintf(stderr,"Could not find the waveform-gtkrc configuration file normally\n"
+ "installed in one of the following places:\n"
+
+ "\t./waveform-gtkrc\n"
+ "\t$(SPECTRUM_RCDIR)/waveform-gtkrc\n"
+ "\t~/.spectrum/waveform-gtkrc\n\t"
+ ETCDIR"/wavegform-gtkrc\n"
+ "This configuration file is used to tune the color, font and other detail aspects\n"
+ "of the user interface. Although the viewer will work without it, the UI\n"
+ "appearence will likely make the application harder to use due to missing visual\n"
+ "cues.\n");
+ }
+
+ gtk_rc_add_default_file(ETCDIR"/waveform-gtkrc");
+ if(homedir){
+ char *rcfile="/.waveform-gtkrc";
+ char *homerc=calloc(1,strlen(homedir)+strlen(rcfile)+1);
+ strcat(homerc,homedir);
+ strcat(homerc,rcfile);
+ gtk_rc_add_default_file(homerc);
+ }
+ gtk_rc_add_default_file(".waveform-gtkrc");
+ gtk_rc_add_default_file("waveform-gtkrc");
+ gtk_init (&argc, &argv);
+
+ panel_create(&p);
+ animate_fish(&p);
+
+ /* set up watching the event pipe */
+ {
+ GIOChannel *channel = g_io_channel_unix_new (eventpipe[0]);
+ guint id;
+
+ g_io_channel_set_encoding (channel, NULL, NULL);
+ g_io_channel_set_buffered (channel, FALSE);
+ g_io_channel_set_close_on_unref (channel, TRUE);
+
+ id = g_io_add_watch (channel, G_IO_IN, async_event_handle, &p);
+
+ g_io_channel_unref (channel);
+
+ }
+
+ /* we want to be running by default */
+ {
+ pthread_t thread_id;
+ animate_fish(&p);
+ process_active=1;
+ pthread_create(&thread_id,NULL,&process_thread,NULL);
+ }
+
+ gtk_main ();
+
+}
+
Added: trunk/spectrum/wave_plot.c
===================================================================
--- trunk/spectrum/wave_plot.c (rev 0)
+++ trunk/spectrum/wave_plot.c 2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,527 @@
+/*
+ *
+ * gtk2 waveform viewer
+ *
+ * Copyright (C) 2004 Monty
+ *
+ * This analyzer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The analyzer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Postfish; see the file COPYING. If not, write to the
+ * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#include "waveform.h"
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <gdk/gdkkeysyms.h>
+#include "wave_plot.h"
+
+static GtkDrawingAreaClass *parent_class = NULL;
+
+static void compute_metadata(GtkWidget *widget){
+ Plot *p=PLOT(widget);
+ int width=widget->allocation.width-p->padx;
+ int i,j;
+
+ p->xgrids=10;
+ p->xtics=30;
+
+ for(i=0;i<p->xgrids;i++)
+ p->xgrid[i]=rint(i/(float)(p->xgrids) * (width-1))+p->padx;
+
+ for(i=0,j=0;i<p->xtics;i++,j++){
+ if(j%4==0)j++;
+ p->xtic[i]=rint(j/(float)(p->xgrids*4) * (width-1))+p->padx;
+ }
+}
+
+GdkColor chcolor(int ch){
+ GdkColor rgb={0,0,0,0};
+
+ switch(ch%7){
+ case 0:
+ rgb.red=0x4000;
+ rgb.green=0x4000;
+ rgb.blue=0x4000;
+ break;
+ case 1:
+ rgb.red=0xd000;
+ rgb.green=0x0000;
+ rgb.blue=0x0000;
+ break;
+ case 2:
+ rgb.red=0x0000;
+ rgb.green=0xb000;
+ rgb.blue=0x0000;
+ break;
+ case 3:
+ rgb.red=0x0000;
+ rgb.green=0x0000;
+ rgb.blue=0xf000;
+ break;
+ case 4:
+ rgb.red=0xc000;
+ rgb.green=0xc000;
+ rgb.blue=0x0000;
+ break;
+ case 5:
+ rgb.red=0x0000;
+ rgb.green=0xc000;
+ rgb.blue=0xc000;
+ break;
+ case 6:
+ rgb.red=0xc000;
+ rgb.green=0x0000;
+ rgb.blue=0xe000;
+ break;
+ }
+
+ return rgb;
+}
+
+static void draw(GtkWidget *widget){
+ int i;
+ Plot *p=PLOT(widget);
+ int height=widget->allocation.height;
+ int width=widget->allocation.width;
+ GtkWidget *parent=gtk_widget_get_parent(widget);
+ int padx = p->padx;
+
+ if(!p->drawgc){
+ p->drawgc=gdk_gc_new(p->backing);
+ gdk_gc_copy(p->drawgc,widget->style->black_gc);
+ }
+
+ /* clear the old rectangle out */
+ {
+ GdkGC *gc=parent->style->bg_gc[0];
+ gdk_draw_rectangle(p->backing,gc,1,0,0,padx,height);
+ gdk_draw_rectangle(p->backing,gc,1,0,height-p->pady,width,p->pady);
+
+ gc=parent->style->white_gc;
+ gdk_draw_rectangle(p->backing,gc,1,padx,0,width-padx,height-p->pady+1);
+ }
+
+ /* draw the light x grid */
+ {
+ int i;
+ GdkColor rgb={0,0,0,0};
+
+ rgb.red=0xc000;
+ rgb.green=0xff00;
+ rgb.blue=0xff00;
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+ for(i=0;i<p->xtics;i++)
+ gdk_draw_line(p->backing,p->drawgc,p->xtic[i],0,p->xtic[i],height-p->pady);
+ }
+
+ PangoLayout **proper=p->x_layout[p->spanchoice];
+
+ for(i=0;i<p->xgrids;i++){
+ int px,py;
+ pango_layout_get_pixel_size(proper[i],&px,&py);
+
+ gdk_draw_layout (p->backing,
+ widget->style->black_gc,
+ p->xgrid[i]-(px/2), height-py+2,
+ proper[i]);
+ }
+
+
+ /* draw the light y grid */
+ {
+ GdkColor rgb={0,0,0,0};
+ int center = (height-p->pady)/2;
+
+ rgb.red=0xc000;
+ rgb.green=0xff00;
+ rgb.blue=0xff00;
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+ for(i=1;i<8;i++){
+ int y1=rint(center + center*i/9);
+ int y2=rint(center - center*i/9);
+ gdk_draw_line(p->backing,p->drawgc,padx,y1,width,y1);
+ gdk_draw_line(p->backing,p->drawgc,padx,y2,width,y2);
+ }
+ }
+
+ /* dark y grid */
+ {
+ GdkColor rgb={0,0,0,0};
+ int center = (height-p->pady)/2;
+ int px,py;
+
+ gdk_draw_line(p->backing,p->drawgc,padx,center-1,width,center-1);
+ gdk_draw_line(p->backing,p->drawgc,padx,center+1,width,center+1);
+
+ rgb.red=0x0000;
+ rgb.green=0xc000;
+ rgb.blue=0xc000;
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+ for(i=-8;i<9;i+=4){
+ int y=rint(center + center*i/9);
+
+ if(i==0)
+ gdk_draw_line(p->backing,widget->style->black_gc,padx,y,width,y);
+ else
+ gdk_draw_line(p->backing,p->drawgc,padx,y,width,y);
+
+ pango_layout_get_pixel_size(p->y_layout[p->rchoice][p->schoice][i/4+2],
+ &px,&py);
+
+ if(i<=0){
+ rgb.red=0x0000;
+ rgb.green=0x0000;
+ rgb.blue=0x0000;
+ }else{
+ rgb.red=0xc000;
+ rgb.green=0x0000;
+ rgb.blue=0x0000;
+ }
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+ gdk_draw_layout (p->backing,
+ p->drawgc,
+ padx-px-2, y-py/2,
+ p->y_layout[p->rchoice][p->schoice][i/4+2]);
+
+ rgb.red=0x0000;
+ rgb.green=0xc000;
+ rgb.blue=0xc000;
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+ }
+ }
+
+ /* dark x grid */
+ {
+ int i;
+ GdkColor rgb={0,0,0,0};
+
+ rgb.red=0x0000;
+ rgb.green=0xc000;
+ rgb.blue=0xc000;
+ gdk_gc_set_rgb_fg_color(p->drawgc,&rgb);
+
+ for(i=0;i<p->xgrids;i++)
+ gdk_draw_line(p->backing,p->drawgc,p->xgrid[i],0,p->xgrid[i],height-p->pady);
+ }
+
+
+ {
+ GdkGCValues values;
+ //gdk_gc_get_values(p->drawgc,&values);
+ values.line_width=2;
+ gdk_gc_set_values(p->drawgc,&values,GDK_GC_LINE_WIDTH);
+ }
+
+ /* draw actual data */
+ if(p->ydata){
+ }
+
+ {
+ GdkGCValues values;
+ //gdk_gc_get_values(p->drawgc,&values);
+ values.line_width=1;
+ gdk_gc_set_values(p->drawgc,&values,GDK_GC_LINE_WIDTH);
+ }
+
+
+}
+
+static void draw_and_expose(GtkWidget *widget){
+ Plot *p=PLOT(widget);
+ if(!GDK_IS_DRAWABLE(p->backing))return;
+ draw(widget);
+ if(!GTK_WIDGET_DRAWABLE(widget))return;
+ if(!GDK_IS_DRAWABLE(widget->window))return;
+ gdk_draw_drawable(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ p->backing,
+ 0, 0,
+ 0, 0,
+ widget->allocation.width,
+ widget->allocation.height);
+}
+
+static gboolean expose( GtkWidget *widget, GdkEventExpose *event ){
+ Plot *p=PLOT(widget);
+ gdk_draw_drawable(widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ p->backing,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+
+ return FALSE;
+}
+
+static void size_request (GtkWidget *widget,GtkRequisition *requisition){
+ Plot *p=PLOT(widget);
+ requisition->width = 400;
+ requisition->height = 200;
+ int axisy=0,axisx=0,pady=0,padx=0,phax=0,px,py,i;
+
+ /* find max X layout */
+ {
+ int max=0;
+ int maxy=0;
+ for(i=0;p->x_layout[1][i];i++){
+ pango_layout_get_pixel_size(p->x_layout[1][i],&px,&py);
+ if(px>max)max=px;
+ if(py>pady)pady=py;
+ if(py>maxy)maxy=py;
+ }
+ max+=maxy*2.;
+ max*=i+1;
+ if(axisx<max)axisx=max;
+ }
+
+ /* find max Y layout */
+ {
+ int max=0;
+ for(i=0;p->y_layout[5][4][i];i++){
+ pango_layout_get_pixel_size(p->y_layout[5][4][i],&px,&py);
+ if(py>max)max=py;
+ if(px>padx)padx=px;
+ }
+ axisy=(max)*8;
+ if(axisy<max)axisy=max;
+ }
+
+ if(requisition->width<axisx+padx)requisition->width=axisx+padx;
+ if(requisition->height<axisy+pady)requisition->height=axisy+pady;
+ p->padx=padx;
+ p->pady=pady;
+ p->phax=phax;
+}
+
+static gboolean configure(GtkWidget *widget, GdkEventConfigure *event){
+ Plot *p=PLOT(widget);
+
+ if (p->backing)
+ g_object_unref(p->backing);
+
+ p->backing = gdk_pixmap_new(widget->window,
+ widget->allocation.width,
+ widget->allocation.height,
+ -1);
+
+ p->ydata=NULL;
+ p->configured=1;
+
+ compute_metadata(widget);
+ plot_refresh(p);
+ draw_and_expose(widget);
+
+ return TRUE;
+}
+
+static void plot_class_init (PlotClass *class){
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+ parent_class = g_type_class_peek_parent (class);
+
+ widget_class->expose_event = expose;
+ widget_class->configure_event = configure;
+ widget_class->size_request = size_request;
+}
+
+static void plot_init (Plot *p){
+}
+
+GType plot_get_type (void){
+ static GType m_type = 0;
+ if (!m_type){
+ static const GTypeInfo m_info={
+ sizeof (PlotClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) plot_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (Plot),
+ 0,
+ (GInstanceInitFunc) plot_init,
+ 0
+ };
+
+ m_type = g_type_register_static (GTK_TYPE_DRAWING_AREA, "Plot", &m_info, 0);
+ }
+
+ return m_type;
+}
+
+GtkWidget* plot_new (int size, int groups, int *channels, int *rate){
+ GtkWidget *ret= GTK_WIDGET (g_object_new (plot_get_type (), NULL));
+ Plot *p=PLOT(ret);
+ int g,i,j;
+ int ch=0;
+ p->groups = groups;
+ for(g=0;g<groups;g++)
+ ch+=channels[g];
+
+ p->total_ch = ch;
+
+ p->ch=channels;
+ p->rate=rate;
+ p->size=size;
+
+ /* generate all the text layouts we'll need */
+ /* linear X scale */
+ {
+ char *labels[13][11]={
+ {"","100ms","200ms","300ms","400ms","500ms","600ms","700ms","800ms","900ms",""},
+ {"","50ms","100ms","150ms","200ms","250ms","300ms","350ms","400ms","450ms",""},
+ {"","20ms","40ms","60ms","80ms","100ms","120ms","140ms","160ms","180ms",""},
+ {"","10ms","20ms","30ms","40ms","50ms","60ms","70ms","80ms","90ms",""},
+ {"","5ms","10ms","15ms","20ms","25ms","30ms","35ms","40ms","45ms",""},
+ {"","2ms","4ms","6ms","8ms","10ms","12ms","14ms","16ms","18ms",""},
+ {"","1ms","2ms","3ms","4ms","5ms","6ms","7ms","8ms","9ms",""},
+ {"","100\xC2\xBCs","200\xC2\xBCs","300\xC2\xBCs","400\xC2\xBCs","500\xC2\xBCs",
+ "600\xC2\xBCs","700\xC2\xBCs","800\xC2\xBCs","900\xC2\xBCs",""},
+ {"","50\xC2\xBCs","100\xC2\xBCs","150\xC2\xBCs","200\xC2\xBCs","250\xC2\xBCs",
+ "300\xC2\xBCs","350\xC2\xBCs","400\xC2\xBCs","450\xC2\xBCs",""},
+ {"","20\xC2\xBCs","40\xC2\xBCs","60\xC2\xBCs","80\xC2\xBCs","100\xC2\xBCs",
+ "120\xC2\xBCs","140\xC2\xBCs","160\xC2\xBCs","180\xC2\xBCs",""},
+ {"","10\xC2\xBCs","20\xC2\xBCs","30\xC2\xBCs","40\xC2\xBCs","50\xC2\xBCs",
+ "60\xC2\xBCs","70\xC2\xBCs","80\xC2\xBCs","90\xC2\xBCs",""},
+ {"","5\xC2\xBCs","10\xC2\xBCs","15\xC2\xBCs","20\xC2\xBCs","25\xC2\xBCs",
+ "30\xC2\xBCs","35\xC2\xBCs","40\xC2\xBCs","45\xC2\xBCs",""},
+ {"","2\xC2\xBCs","4\xC2\xBCs","6\xC2\xBCs","8\xC2\xBCs","10\xC2\xBCs",
+ "12\xC2\xBCs","14\xC2\xBCs","16\xC2\xBCs","18\xC2\xBCs",""}};
+
+ p->x_layout=calloc(13,sizeof(*p->x_layout));
+ for(i=0;i<12;i++){
+ p->x_layout[i]=calloc(11,sizeof(**p->x_layout));
+ for(j=0;j<10;j++)
+ p->x_layout[i][j]=gtk_widget_create_pango_layout(ret,labels[i][j]);
+ }
+ }
+
+ /* phase Y scale */
+ {
+ char *label1[6] = {"1",".5",".2",".1",".01",".001"};
+ char *label1a[6] = {".5",".25",".1",".05",".005",".0005"};
+
+ char *labeln1[6] = {"-1","-.5","-.2","-.1","-.01","-.001"};
+ char *labeln1a[6] = {"-.5","-.25","-.1","-.05","-.005","-.0005"};
+
+ char *label2[6] = {"0dB","-6dB","-14dB","-20dB","-40dB","-60dB"};
+ char *label3[5] = {"0","-65dB","-96dB","-120dB","-160dB"};
+
+ int val2[6] = {0,-6,-14,-20,-40,-60};
+ int val3[5] = {0,-65,-96,-120,-160};
+
+ p->y_layout=calloc(6,sizeof(*p->y_layout));
+ for(i=0;i<6;i++){
+ p->y_layout[i]=calloc(5,sizeof(**p->y_layout));
+ p->y_layout[i][0]=calloc(6,sizeof(***p->y_layout));
+
+ p->y_layout[i][0][0]=gtk_widget_create_pango_layout(ret,label1[i]);
+ p->y_layout[i][0][1]=gtk_widget_create_pango_layout(ret,label1a[i]);
+ p->y_layout[i][0][2]=gtk_widget_create_pango_layout(ret,label3[0]);
+ p->y_layout[i][0][3]=gtk_widget_create_pango_layout(ret,labeln1a[i]);
+ p->y_layout[i][0][4]=gtk_widget_create_pango_layout(ret,labeln1[i]);
+
+ for(j=1;j<5;j++){
+ char buf[10];
+ p->y_layout[i][j]=calloc(6,sizeof(***p->y_layout));
+ p->y_layout[i][j][0]=gtk_widget_create_pango_layout(ret,label2[i]);
+ p->y_layout[i][j][2]=gtk_widget_create_pango_layout(ret,label3[j]);
+ p->y_layout[i][j][4]=gtk_widget_create_pango_layout(ret,label2[i]);
+ sprintf(buf,"%ddB",(val2[i]+val3[j])/2);
+ p->y_layout[i][j][1]=gtk_widget_create_pango_layout(ret,buf);
+ p->y_layout[i][j][3]=gtk_widget_create_pango_layout(ret,buf);
+ }
+ }
+ }
+
+ p->ch_active=calloc(ch,sizeof(*p->ch_active));
+
+ plot_clear(p);
+ return ret;
+}
+
+void plot_refresh (Plot *p){
+ float ymax,pmax,pmin;
+ int width=GTK_WIDGET(p)->allocation.width-p->padx;
+ int height=GTK_WIDGET(p)->allocation.height-p->pady;
+ float **data;
+ float *floor;
+
+ if(!p->configured)return;
+
+ data = process_fetch(p->blockslice, p->overslice, p->span);
+ p->ydata=data;
+}
+
+void plot_clear (Plot *p){
+ GtkWidget *widget=GTK_WIDGET(p);
+ int width=GTK_WIDGET(p)->allocation.width-p->padx;
+ int i,j;
+
+ if(p->ydata)
+ for(i=0;i<p->total_ch;i++)
+ for(j=0;j<p->size;j++)
+ p->ydata[i][j]=NAN;
+ draw_and_expose(widget);
+}
+
+float **plot_get (Plot *p){
+ return(p->ydata);
+}
+
+void plot_setting (Plot *p, int range, int scale, int interval, int span, int rangechoice, int scalechoice, int spanchoice,
+ int *blockslice, int *overslice){
+ GtkWidget *widget=GTK_WIDGET(p);
+ p->range=range;
+ p->scale=scale;
+ p->span=span;
+ p->interval=interval;
+ p->rchoice=rangechoice;
+ p->schoice=scalechoice;
+ p->spanchoice=spanchoice;
+
+ if(blockslice){
+ if(!p->blockslice)
+ p->blockslice=calloc(p->groups,sizeof(*p->blockslice));
+ memcpy(p->blockslice, blockslice, p->groups*sizeof(*p->blockslice));
+ }
+ if(overslice){
+ if(!p->overslice)
+ p->overslice=calloc(p->groups,sizeof(*p->overslice));
+ memcpy(p->overslice, overslice, p->groups*sizeof(*p->blockslice));
+ }
+
+ compute_metadata(widget);
+ plot_refresh(p);
+ draw_and_expose(widget);
+}
+
+void plot_draw (Plot *p){
+ GtkWidget *widget=GTK_WIDGET(p);
+ draw_and_expose(widget);
+}
+
+void plot_set_active(Plot *p, int *a){
+ GtkWidget *widget=GTK_WIDGET(p);
+ memcpy(p->ch_active,a,p->total_ch*sizeof(*a));
+ plot_refresh(p);
+ draw_and_expose(widget);
+}
+
Added: trunk/spectrum/wave_plot.h
===================================================================
--- trunk/spectrum/wave_plot.h (rev 0)
+++ trunk/spectrum/wave_plot.h 2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,118 @@
+/*
+ *
+ * gtk2 waveform viewer
+ *
+ * Copyright (C) 2004 Monty
+ *
+ * This analyzer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The analyzer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Postfish; see the file COPYING. If not, write to the
+ * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#ifndef __PLOT_H__
+#define __PLOT_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtksignal.h>
+#include <gtk/gtkdrawingarea.h>
+
+G_BEGIN_DECLS
+
+#define PLOT_TYPE (plot_get_type ())
+#define PLOT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PLOT_TYPE, Plot))
+#define PLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PLOT_TYPE, PlotClass))
+#define IS_PLOT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PLOT_TYPE))
+#define IS_PLOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PLOT_TYPE))
+
+typedef struct _Plot Plot;
+typedef struct _PlotClass PlotClass;
+
+struct _Plot{
+
+ GtkDrawingArea canvas;
+ GdkPixmap *backing;
+ GdkGC *drawgc;
+
+ PangoLayout ***x_layout;
+ PangoLayout ****y_layout;
+
+ int configured;
+ float **ydata;
+ int size;
+
+ int groups;
+ int *ch;
+ int *ch_active;
+ int total_ch;
+
+ int range;
+ int scale;
+ int interval;
+ int span;
+
+ int rchoice;
+ int schoice;
+ int spanchoice;
+
+ int *blockslice;
+ int *overslice;
+
+ int *rate;
+
+ int xgrid[20];
+ int xgrids;
+ int xtic[200];
+ int xtics;
+
+ int ygrid[5];
+ int ygrids;
+ int ytic[50];
+ int ytics;
+
+ float padx;
+ float phax;
+ float pady;
+
+};
+
+struct _PlotClass{
+
+ GtkDrawingAreaClass parent_class;
+ void (* plot) (Plot *m);
+};
+
+GType plot_get_type (void);
+GtkWidget* plot_new (int n, int inputs, int *channels, int *rate);
+void plot_refresh (Plot *m);
+void plot_setting (Plot *p, int range, int scale, int interval, int span,
+ int rangechoice, int scalechoice, int spanchoice,
+ int *blockslice, int *overslice);
+void plot_draw (Plot *m);
+void plot_clear (Plot *m);
+int plot_width (Plot *m);
+float** plot_get (Plot *m);
+void plot_set_active (Plot *m, int *);
+
+GdkColor chcolor(int ch);
+
+G_END_DECLS
+
+#endif
+
+
+
+
Added: trunk/spectrum/wave_process.c
===================================================================
--- trunk/spectrum/wave_process.c (rev 0)
+++ trunk/spectrum/wave_process.c 2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,115 @@
+/*
+ *
+ * gtk2 waveform viewer
+ *
+ * Copyright (C) 2004 Monty
+ *
+ * This analyzer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The analyzer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Postfish; see the file COPYING. If not, write to the
+ * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#include "waveform.h"
+#include "io.h"
+
+pthread_mutex_t feedback_mutex=PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+int feedback_increment=0;
+float **buffercopies;
+float **plot_data=0;
+
+sig_atomic_t process_active=0;
+sig_atomic_t process_exit=0;
+
+sig_atomic_t acc_clear=0;
+sig_atomic_t acc_rewind=0;
+sig_atomic_t acc_loop=0;
+
+static void init_process(void){
+ int i;
+ if(plot_data==NULL){
+ plot_data=calloc(total_ch,sizeof(*plot_data));
+ buffercopies=calloc(total_ch,sizeof(*buffercopies));
+ for(i=0;i<total_ch;i++){
+ plot_data[i]=calloc(blocksize,sizeof(**plot_data));
+ buffercopies[i]=calloc(blocksize,sizeof(**buffercopies));
+ }
+ }
+}
+
+/* return 0 on EOF, 1 otherwise */
+static int process(){
+ int fi,ch,i;
+
+ if(acc_rewind)
+ rewind_files();
+ acc_rewind=0;
+
+ if(input_read(acc_loop,1))
+ return 0;
+
+ pthread_mutex_lock(&feedback_mutex);
+ ch=0;
+ for(fi=0;fi<inputs;fi++){
+ for(i=ch;i<ch+channels[fi];i++)
+ memcpy(buffercopies[i],blockbuffer[i],blocksize*sizeof(**buffercopies));
+ ch+=channels[fi];
+ }
+ pthread_mutex_unlock(&feedback_mutex);
+
+ feedback_increment++;
+ write(eventpipe[1],"",1);
+ return 1;
+}
+
+void *process_thread(void *dummy){
+ while(!process_exit && process());
+ process_active=0;
+ write(eventpipe[1],"",1);
+ return NULL;
+}
+
+float **process_fetch(int *blockslice,int *overslice,int span){
+ int fi,i,j,k,ch;
+
+ init_process();
+ if(!blockslice || !overslice)return NULL;
+
+ /* by channel */
+ ch=0;
+ for(fi=0;fi<inputs;fi++){
+ /* When the blockslice and overslice are equal, the data
+ buffer contains only one copy of the span. When the
+ overslice is much smaller than the blockslice, we have
+ ceil(blockslice/overslice) back-to-back spans */
+
+ int copies = (int)ceil(blockslice[fi]/overslice[fi]);
+ int spann = rate[fi]*span/1000000;
+
+ for(i=ch;i<ch+channels[fi];i++){
+ int offset=0;
+ float *plotdatap=plot_data[i];
+ pthread_mutex_lock(&feedback_mutex);
+ for(j=0;j<copies;j++){
+ float *data=blockbuffer[i]+offset;
+ for(k=0;k<spann;k++)
+ *(plotdatap++)=data[k];
+ offset+=overslice[fi];
+ }
+ }
+ ch+=channels[fi];
+ }
+
+ return plot_data;
+}
Added: trunk/spectrum/waveform.c
===================================================================
--- trunk/spectrum/waveform.c (rev 0)
+++ trunk/spectrum/waveform.c 2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,288 @@
+/*
+ *
+ * gtk2 waveform viewer
+ *
+ * Copyright (C) 2004 Monty
+ *
+ * This analyzer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The analyzer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Postfish; see the file COPYING. If not, write to the
+ * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#include "waveform.h"
+#include <signal.h>
+#include <getopt.h>
+#include <fenv.h> // Thank you C99!
+#include <gtk/gtk.h>
+#include "version.h"
+#include "io.h"
+
+int eventpipe[2];
+char *version;
+char *inputname[MAX_FILES];
+int inputs=0;
+int blocksize = 131072; /* starting default */
+
+void handler(int sig){
+ signal(sig,SIG_IGN);
+ if(sig!=SIGINT){
+ fprintf(stderr,
+ "\nTrapped signal %d; exiting!\n"
+ "This signal almost certainly indicates a bug in the analyzer;\n"
+ "If this version of the analyzer is newer than a few months old,\n"
+ "please email a detailed report of the crash along with\n"
+ "processor type, OS version, and as much information as possible\n"
+ "about what caused the crash. The best possible report will\n"
+ "outline the exact steps needed to reproduce the error, ensuring\n"
+ "that I can fix the bug as quickly as possible.\n\n"
+ "-- monty at xiph.org, spectrum revision %s\n\n",sig,version);
+ }
+
+ gtk_main_quit();
+}
+
+const char *optstring = "-r:c:EeBlb:suh";
+
+struct option options [] = {
+ {"rate",required_argument,NULL,'r'},
+ {"channels",required_argument,NULL,'c'},
+ {"big-endian",no_argument,NULL,'E'},
+ {"little-endian",no_argument,NULL,'e'},
+ {"bits",required_argument,NULL,'b'},
+ {"signed",no_argument,NULL,'s'},
+ {"unsigned",no_argument,NULL,'u'},
+ {"help",no_argument,NULL,'h'},
+
+ {NULL,0,NULL,0}
+};
+
+static void usage(FILE *f){
+ fprintf( f,
+"\ngtk2 waveform viewer, revision %s\n\n"
+
+"USAGE:\n\n"
+" waveform [options] [file]\n\n"
+
+"OPTIONS:\n\n"
+" -b --bits <bits> : Force input to be read as 8, 16, 24 or 32 bit\n"
+" PCM. Default bit depth is normally read from\n"
+" the file/stream header or set to 16 bits\n"
+" for raw input.\n"
+" -B -E --big-endian : Force input to be read as big endian.\n"
+" Default endianness is normally read from the\n"
+" file/stream header or set to host"
+" endianness for raw input.\n"
+" -c --channels <channels> : Input channel number override; use to\n"
+" specify the number of channels in a raw\n"
+" input. default: 1\n"
+" -e -l --little-endian : Force input to be read as little endian.\n"
+" Default endianness is normally read from the\n"
+" file/stream header or set to host"
+" endianness for raw input.\n"
+" -h --help : print this help\n"
+" -r --rate <Hz> : Input sample rate override in Hz; use to\n"
+" specify the rate of a raw input.\n"
+" default: 44100\n"
+" -s --signed : Force input to be read as signed PCM.\n"
+" Signedness is normally read from the \n"
+" file/stream header or set to signed for raw\n"
+" input.\n"
+" -u --unsigned : Force input to be read as unsigned PCM.\n"
+" Signedness is normally read from the \n"
+" file/stream header or set to signed for raw\n"
+" input.\n\n"
+
+"INPUT:\n\n"
+
+" Waveform takes raw, WAV or AIFF input either from stdin or from \n"
+" file[s]/stream[s] specified on the command line.\n\n",version);
+
+}
+
+void parse_command_line(int argc, char **argv){
+ int c,long_option_index;
+
+ while((c=getopt_long(argc,argv,optstring,options,&long_option_index))!=EOF){
+ switch(c){
+ case 1:
+ /* file name that belongs to current group */
+ if(inputs>=MAX_FILES){
+ fprintf(stderr,"Maximum of MAX_FILES input files exceeded. Oops. Programmer was lazy.\n\n");
+ exit(1);
+ }
+ inputname[inputs++]=strdup(optarg);
+ break;
+ case 'b':
+ /* force bit width */
+ {
+ int a=atoi(optarg);
+ bits[inputs]=a;
+ if(a!=8 && a!=16 && a!=24 && a!=32){
+ usage(stderr);
+ exit(1);
+ }
+ }
+ break;
+ case 'B':case 'E':
+ /* force big endian */
+ bigendian[inputs]=1;
+ break;
+ case 'c':
+ /* force channels */
+ {
+ int a=atoi(optarg);
+ channels[inputs]=a;
+ if(a<1 || a>32){
+ usage(stderr);
+ exit(1);
+ }
+ }
+ break;
+ case 'l':case 'e':
+ /* force little endian */
+ bigendian[inputs]=0;
+ break;
+ case 'h':
+ usage(stdout);
+ exit(0);
+ case 'r':
+ /* force rate */
+ {
+ int a=atoi(optarg);
+ rate[inputs]=a;
+ if(a<4000 || a>200000){
+ usage(stderr);
+ exit(1);
+ }
+ }
+ break;
+ case 's':
+ /* force signed */
+ signedp[inputs]=1;
+ break;
+ case 'u':
+ /* force unsigned */
+ signedp[inputs]=0;
+ break;
+ default:
+ usage(stderr);
+ exit(0);
+ }
+ }
+}
+
+static int sigill=0;
+void sigill_handler(int sig){
+ /* make sure */
+ if(sig==SIGILL)sigill=1;
+}
+
+int main(int argc, char **argv){
+ int fi;
+
+ version=strstr(VERSION,"version.h");
+ if(version){
+ char *versionend=strchr(version,' ');
+ if(versionend)versionend=strchr(versionend+1,' ');
+ if(versionend)versionend=strchr(versionend+1,' ');
+ if(versionend)versionend=strchr(versionend+1,' ');
+ if(versionend){
+ int len=versionend-version-9;
+ version=strdup(version+10);
+ version[len-1]=0;
+ }
+ }else{
+ version="";
+ }
+
+ /* parse command line and open all the input files */
+ parse_command_line(argc, argv);
+
+ /* We do not care about FPEs; rather, underflow is nominal case, and
+ its better to ignore other traps in production than to crash the
+ app. Please inform the FPU of this. */
+
+#ifndef DEBUG
+ fedisableexcept(FE_INVALID);
+ fedisableexcept(FE_INEXACT);
+ fedisableexcept(FE_UNDERFLOW);
+ fedisableexcept(FE_OVERFLOW);
+#else
+ feenableexcept(FE_INVALID);
+ feenableexcept(FE_INEXACT);
+ feenableexcept(FE_UNDERFLOW);
+ feenableexcept(FE_OVERFLOW);
+#endif
+
+ /* Linux Altivec support has a very annoying problem; by default,
+ math on denormalized floats will simply crash the program. FFTW3
+ uses Altivec, so boom, but only random booms.
+
+ By the C99 spec, the above exception configuration is also
+ supposed to handle Altivec config, but doesn't. So we use the
+ below ugliness to both handle altivec and non-alitvec PPC. */
+
+#ifdef __PPC
+#include <altivec.h>
+ signal(SIGILL,sigill_handler);
+
+#if (defined __GNUC__) && (__GNUC__ == 3) && ! (defined __APPLE_CC__)
+ __vector unsigned short noTrap =
+ (__vector unsigned short){0,0,0,0,0,0,0x1,0};
+#else
+ vector unsigned short noTrap =
+ (vector unsigned short)(0,0,0,0,0,0,0x1,0);
+#endif
+
+ vec_mtvscr(noTrap);
+#endif
+
+ /* easiest way to inform gtk of changes and not deal with locking
+ issues around the UI */
+ if(pipe(eventpipe)){
+ fprintf(stderr,"Unable to open event pipe:\n"
+ " %s\n",strerror(errno));
+
+ exit(1);
+ }
+
+ /* Allows event compression on the read side */
+ if(fcntl(eventpipe[0], F_SETFL, O_NONBLOCK)){
+ fprintf(stderr,"Unable to set O_NONBLOCK on event pipe:\n"
+ " %s\n",strerror(errno));
+
+ exit(1);
+ }
+
+ //signal(SIGINT,handler);
+ signal(SIGSEGV,handler);
+
+ if(input_load())exit(1);
+
+ /* set block size equal to maximum input rate */
+ /* (maximum display width: 1s, maximum update interval 1s) */
+ blocksize=0;
+ for(fi=0;fi<inputs;fi++)
+ if(rate[fi]>blocksize)blocksize=rate[fi];
+
+ /* begin with a display width of 1s */
+ /* begin with an update interval (blockslice) of 100ms */
+ for(fi=0;fi<inputs;fi++)
+ blockslice[fi]=rate[fi]/10;
+
+ panel_go(argc,argv);
+
+ return(0);
+}
Added: trunk/spectrum/waveform.h
===================================================================
--- trunk/spectrum/waveform.h (rev 0)
+++ trunk/spectrum/waveform.h 2012-04-12 14:29:17 UTC (rev 18229)
@@ -0,0 +1,96 @@
+/*
+ *
+ * gtk2 spectrum analyzer
+ *
+ * Copyright (C) 2004 Monty
+ *
+ * This analyzer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * The analyzer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Postfish; see the file COPYING. If not, write to the
+ * Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ */
+
+#ifndef _WAVEFORM_H_
+#define _WAVEFORM_H_
+
+#define _GNU_SOURCE
+#define _ISOC99_SOURCE
+#define _FILE_OFFSET_BITS 64
+#define _REENTRANT 1
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#define __USE_GNU 1
+#include <pthread.h>
+#include <string.h>
+#include <math.h>
+#include <signal.h>
+#include <fcntl.h>
+
+#define MAX_FILES 16
+#define readbuffersize 8192
+
+extern int blocksize;
+
+static inline float todB(float x){
+ return logf((x)*(x)+1e-30f)*4.34294480f;
+}
+
+#ifdef UGLY_IEEE754_FLOAT32_HACK
+
+static inline float todB_a(const float *x){
+ union {
+ int32_t i;
+ float f;
+ } ix;
+ ix.f = *x;
+ ix.i = ix.i&0x7fffffff;
+ return (float)(ix.i * 7.17711438e-7f -764.6161886f);
+}
+
+#else
+
+static inline float todB_a(const float *x){
+ return todB(*x);
+}
+
+#endif
+
+#ifndef max
+#define max(x,y) ((x)>(y)?(x):(y))
+#endif
+
+
+#define toOC(n) (log(n)*1.442695f-5.965784f)
+#define fromOC(o) (exp(((o)+5.965784f)*.693147f))
+
+extern int eventpipe[2];
+
+extern void panel_go(int argc,char *argv[]);
+extern void *process_thread(void *dummy);
+extern float **process_fetch(int *, int *, int);
+
+extern pthread_mutex_t feedback_mutex;
+extern int feedback_increment;
+extern float **feedback_instant;
+extern sig_atomic_t acc_rewind;
+extern sig_atomic_t acc_loop;
+
+extern sig_atomic_t process_active;
+extern sig_atomic_t process_exit;
+
+#endif
+
More information about the commits
mailing list