/* */ // AppSpect - Spectrogram Applet // // Java (c) 1998 Mark Huckvale University College London // // Displays a spectrogram from a WAV file // // parameters: // file signal filename // stime start time // etime end time // timescale y/n // freqscale y/n // border y/n import java.util.*; import java.io.*; import java.awt.*; import java.applet.*; import java.awt.image.*; import java.net.*; // Read a WAV file into memory class WAVFile { public short buffer[]; public int length; public int srate; public int min=-1; public int max=1; private static int reverseint(int val) { return ((val >> 24) & 0xFF) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | ((val << 24) & 0xFF000000); } public WAVFile(URL furl,double stime,double etime) { try { URLConnection fcon = furl.openConnection(); InputStream fip = fcon.getInputStream(); BufferedInputStream bip = new BufferedInputStream(fip); DataInputStream dip = new DataInputStream(bip); int[] head = new int[11]; for (int i=0;i<11;i++) head[i] = reverseint(dip.readInt()); length = head[10]/2; srate = head[6]; int startf=(int)(stime*srate); if (startf > length) startf=length; int numf; if (etime < 0) numf = length - startf; else numf = (int)((etime-stime)*srate); if (numf < 0) numf=0; if ((startf+numf)>length) numf = length-startf; buffer = new short[numf]; for (int i=0;i max) max = val; if (val < min) min = val; buffer[i] = (short)val; } length = numf; dip.close(); bip.close(); fip.close(); } catch (Exception e) { System.out.println("File error"); } } }; // Forward Real Fast Fourier Transform // // Converted from 'Numerical Recipes in C' // // Java (c) 1998 Mark Huckvale University College London class FFT { /* * FFT routine based on one originally written by N. Brenner of Lincoln * Laboratories. * On entry: 'data[1..2*nn]' contains 'nn' complex data points, with each * complex value occupying two consecutive locations, real * value stored in the lower address. 'nn' MUST be an integer * power of 2 (this is not checked for). * On exit: 'data[1..2*nn]' contains the complex transformation output, * again stored in pairs of real and imaginary values. */ private static final void four1(float data[],int nn) { int n,mmax,m,j,istep,i; double wtemp,wr,wpr,wpi,wi,theta; /* double precision for the */ /* trigonometric recurrences. */ float tempr,tempi; n=nn << 1; j=1; /* Bit-Reversal routine */ for (i = 1; i < n; i += 2) { if (j > i) { /* exchange the two complex numbers */ tempr=data[j]; data[j]=data[i]; data[i]=tempr; tempi=data[j+1]; data[j+1]=data[i+1]; data[i+1]=tempi; } m = n >> 1; while (m >= 2 && j > m) { j -= m; m >>= 1; } j += m; } /* Danielson-Lanczos loops */ mmax = 2; while (n > mmax) { /* outer loop executed ln(nn) times */ istep = 2*mmax; /* initialise for the trigonometric recurrence */ theta = 2*Math.PI/(mmax); wtemp = Math.sin(0.5*theta); wpr = -2.0*wtemp*wtemp; wpi = Math.sin(theta); wr = 1.0; wi = 0.0; /* two nested inner loops */ for (m = 1; m < mmax; m += 2) { for (i = m; i <= n; i += istep) { /* Danielson-Lanczos formula: */ j = i+mmax; tempr = (float)(wr*data[j]-wi*data[j+1]); tempi = (float)(wr*data[j+1]+wi*data[j]); data[j] = data[i]-tempr; data[j+1] = data[i+1]-tempi; data[i] += tempr; data[i+1] += tempi; } /* trigonometric recurrence */ wr = (wtemp=wr)*wpr-wi*wpi+wr; wi = wi*wpr+wtemp*wpi+wi; } mmax = istep; } } /* * This routine takes a single real function and calculates its * DFT * On Entry: 'data[1..2n]' contains the real function of length '2n', * where 'n' MUST be an integral power of 2. * On Exit: 'data[1..2n]' contains the positive half of the complex DFT. * 'data[1]' contains the real valued first component and * 'data[2]' contains the real valued last component of the * complex transform. */ public static final void RealFFTPower(float sig[],float mag[],int n) { int i,i1,i2,i3,i4,n2p3; double c1=(double)0.5,c2,h1r,h1i,h2r,h2i; double wr,wi,wpr,wpi,wtemp,theta; /* copy data into temp buffer */ float data[] = new float[n+1]; for (i=1;i<=n;i++) data[i] = sig[i-1]; n = n >> 1; /* Initialise the recurrence. */ theta = Math.PI/(double) n; c2 = (double)-0.5; four1(data,n); wtemp = Math.sin(0.5*theta); wpr = -2.0*wtemp*wtemp; wpi = Math.sin(theta); wr = 1.0+wpr; wi = wpi; n2p3 = 2*n+3; for (i = 2; i <= n/2; i++) { /* Case i==1 done below */ /* Separate transforms from the data */ i4 = 1+(i3=n2p3-(i2=1+(i1=i+i-1))); h1r = c1*(data[i1]+data[i3]); h1i = c1*(data[i2]-data[i4]); h2r = -c2*(data[i2]+data[i4]); h2i = c2*(data[i1]-data[i3]); /* Recombine to form true transform of original data */ data[i1] = (float)(h1r+wr*h2r-wi*h2i); data[i2] = (float)(h1i+wr*h2i+wi*h2r); data[i3] = (float)(h1r-wr*h2r+wi*h2i); data[i4] = (float)(-h1i+wr*h2i+wi*h2r); /* The recurrence Relations */ wr = (wtemp = wr)*wpr-wi*wpi+wr; wi = wi*wpr+wtemp*wpi+wi; } /* Squeeze the first & last data into the original array */ data[1] = (float)((h1r=data[1])+data[2]); data[2] = (float)(h1r-data[2]); for (i=0;i fftsize) wisize = fftsize; } private double calcfloor() { /* get normalisation estimate from speech power */ double xmax = 0.0; double xsum = 0.0; int i,j; int samp=0; for (i=0;i<(wvf.length-wisize);i+=wisize) { xsum = 1; for (j=0;j xmax) xmax = xsum; } return(10.0*Math.log(xmax)/LOGE10 + 6 - 50); } public void run() { int i,j; double xsum; // System.out.println("in calcspect.run(), width="+width); if (wvf==null) return; float xp[] = new float[fftsize]; float sm[] = new float[fftsize/2]; double xmax = -100; double xmin = 100; double omega = 2.0 * Math.PI / (wisize-1); double window[] = new double[wisize]; for (i=0;iwvf.length) numf = wvf.length-start; for (i=0;i0;i--) xp[i] = (float)((xp[i] - 0.95*xp[i-1]) * window[i]); xp[0] *= window[0]; /* do FFT */ FFT.RealFFTPower(xp,sm,fftsize); /* convert to pixels */ xend = xstep; xpos = 0; k=0; for (i=height-1;i>=0;i--) { xsum = 1; cnt = 0; while ((xpos < xend) && (k < fftsize/2)) { xsum += sm[k++]; xpos += 1.0; cnt++; } edb = 10.0*Math.log(xsum/cnt)/LOGE10; pixel = (int)(0.5+15.0*(edb-dbnorm)/50); if (pixel > 15) pixel=15; if (pixel < 0) pixel=0; pix[i*width+j] = greys[pixel]; xend += xstep; } col++; if ((col%32)==0) p.repaint(col-32,0,col,height); } p.repaint(); } } // panel to display spectrogram public class appspect extends Applet { WAVFile wvf; calcspect cspect; double stime=0; double etime=-1; boolean timescale; boolean freqscale; boolean border; Rectangle mnrect; Rectangle tsrect; Rectangle fsrect; Rectangle sprect; int scalesize; int ticsize; Font scalefont; public boolean imageUpdate(Image img,int flags,int x,int y,int w,int h) { return ((flags & ALLBITS) == 0); } public void paint(Graphics g) { update(g); } private String decimal(double val) { int part1 = (int)val; val -= part1; int part2 = (int)(0.5+100.0*val); return(part1+"."+part2); } private void painttimescale(Graphics g) { double t=0.0; int x; g.setColor(Color.white); g.fillRect(tsrect.x,tsrect.y,tsrect.width,tsrect.height); g.setColor(new Color(64,64,64)); g.setFont(scalefont); while (t < etime) { x = (int)(tsrect.width*(t-stime)/(etime-stime)); if ((x >= 0) && (x < tsrect.width)) { g.drawLine(tsrect.x+x,tsrect.y, tsrect.x+x,tsrect.y+ticsize); g.drawString(decimal(t),tsrect.x+x+2,tsrect.y+tsrect.height-1); } t += 0.05; x = (int)(tsrect.width*(t-stime)/(etime-stime)); if ((x >= 0) && (x < tsrect.width)) g.drawLine(tsrect.x+x,tsrect.y, tsrect.x+x,tsrect.y+ticsize/2); t += 0.05; } } private void paintfreqscale(Graphics g) { int f=0; int y; g.setColor(Color.white); g.fillRect(fsrect.x,fsrect.y,fsrect.width,fsrect.height); g.setColor(new Color(64,64,64)); g.setFont(scalefont); while (f < wvf.srate/2) { y = (int)(fsrect.height*f*2.0/wvf.srate); g.drawLine(fsrect.x+fsrect.width-ticsize-1,fsrect.y+fsrect.height-1-y, fsrect.x+fsrect.width-1,fsrect.y+fsrect.height-1-y); g.drawString(""+f/1000,fsrect.x+1,fsrect.y+fsrect.height-3-y); f += 500; y = (int)(fsrect.height*f*2.0/wvf.srate); g.drawLine(fsrect.x+fsrect.width-ticsize-1,fsrect.y+fsrect.height-1-y, fsrect.x+fsrect.width-1,fsrect.y+fsrect.height-1-y); f += 500; } } public void update(Graphics g) { if (wvf==null) return; // get display size setsizes(); cspect.setSize(sprect.width,sprect.height); // get image from calcspect Image img = cspect.getSpect(this); g.drawImage(img,sprect.x,sprect.y,this); // draw timescale if (timescale) painttimescale(g); // draw freqscale if (freqscale) paintfreqscale(g); if (timescale && freqscale) { g.setColor(Color.white); g.fillRect(mnrect.x,tsrect.y,fsrect.width,tsrect.height); } // draw border if (border) { Dimension d=size(); g.setColor(new Color(192,192,192)); g.drawRect(0,0,d.width-1,d.height-1); g.drawRect(3,3,d.width-8,d.height-8); g.setColor(new Color(64,64,64)); g.drawRect(1,1,d.width-2,d.height-2); g.drawRect(4,4,d.width-8,d.height-8); g.setColor(new Color(128,128,128)); g.drawRect(1,1,d.width-3,d.height-3); g.drawRect(2,2,d.width-5,d.height-5); g.drawRect(3,3,d.width-7,d.height-7); } } private void setsizes() { Dimension d = size(); Graphics g = getGraphics(); g.setFont(scalefont); FontMetrics fm = g.getFontMetrics(); scalesize = (5*fm.getHeight())/4; ticsize = scalesize/2; if (border) mnrect = new Rectangle(5,5,d.width-10,d.height-10); else mnrect = new Rectangle(0,0,d.width,d.height); tsrect = new Rectangle(mnrect.x+((freqscale)?scalesize:0), mnrect.y+mnrect.height-scalesize, (freqscale)?mnrect.width-scalesize:mnrect.width, scalesize); fsrect = new Rectangle(mnrect.x,mnrect.y,scalesize, (timescale)?mnrect.height-scalesize:mnrect.height); sprect = new Rectangle(mnrect.x+((freqscale)?scalesize:0), mnrect.y, (freqscale)?mnrect.width-scalesize:mnrect.width, (timescale)?mnrect.height-scalesize:mnrect.height); } public void init() { String stimestr = getParameter("stime"); String etimestr = getParameter("etime"); try { if (stimestr==null) stime=0.0; else stime=new Double(stimestr).doubleValue(); if (etimestr==null) etime=-1.0; else etime=new Double(etimestr).doubleValue(); } catch (NumberFormatException e) { stime=0.0; etime=-1.0; } String flag = getParameter("timescale"); timescale = ((flag!=null) && ((flag.charAt(0)=='y')||(flag.charAt(0)=='Y'))); flag = getParameter("freqscale"); freqscale = ((flag!=null) && ((flag.charAt(0)=='y')||(flag.charAt(0)=='Y'))); flag = getParameter("border"); border = ((flag!=null) && ((flag.charAt(0)=='y')||(flag.charAt(0)=='Y'))); scalefont = new Font("SansSerif",Font.PLAIN,9); setsizes(); String ifname = getParameter("file"); try { URL furl = new URL(getDocumentBase(),ifname); wvf = new WAVFile(furl,stime,etime); Dimension d = size(); cspect = new calcspect(this,wvf,sprect.width,sprect.height); } catch (MalformedURLException e) {} } }