/*
*/
// 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) {}
}
}