/*
DFTAnalyzer.java
(c) 2009-2016 Edward Swartz
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
*/
package ejs.base.sound;
import java.util.Arrays;
/**
* Utility class for constructing DFT from the sound signal
* @author Ed
*
*/
public class DFTAnalyzer {
private int sampleSize;
private double[] xx;
private int xidx;
private double rex[], imx[];
private double mag[], phase[], uwphase[];
private double avgmag[];
public DFTAnalyzer(int sampleBits) {
this.sampleSize = 1 << sampleBits;
this.xx = new double[sampleSize];
this.rex = new double[sampleSize / 2 + 1];
this.imx = new double[sampleSize / 2 + 1];
this.mag = new double[sampleSize / 2 + 1];
this.phase = new double[sampleSize / 2 + 1];
this.avgmag = new double[sampleSize / 2 + 1];
this.uwphase = new double[sampleSize / 2 + 1];
this.xidx = 0;
}
public void send(byte[] signal16le) {
int sidx = 0;
while (sidx < signal16le.length) {
short sample = (short) ((signal16le[sidx++] & 0xff) | (signal16le[sidx++] << 8));
xx[xidx++] = sample / 32768.0;
if (xidx >= xx.length - 1) {
calc();
xidx = 0;
}
}
}
/**
* Calculate the DFT
*/
private void calc() {
// clear accumulators
Arrays.fill(rex, (double) 0.0);
Arrays.fill(imx, (double) 0.0);
// compute DFT, applying a Hamming window
for (int k = 0; k <= sampleSize / 2; k++) {
double w = 0.54 - 0.46 * Math.cos(2 * Math.PI * k / (sampleSize / 2));
double phase = 2 * Math.PI * k / xx.length;
for (int i = 0; i < xx.length; i++) {
double ang = phase * i;
rex[k] += xx[i] * w * Math.cos(ang);
imx[k] -= xx[i] * w * Math.sin(ang);
}
}
// convert to polar notation
System.out.println();
double maxMag = 0;
for (int k = 0; k <= sampleSize / 2 ; k++) {
mag[k] = Math.sqrt(rex[k]*rex[k] + imx[k]*imx[k]);
// running average of magnitude
avgmag[k] = (avgmag[k] + mag[k]) / 2;
if (avgmag[k] > maxMag)
maxMag = avgmag[k];
//if (rex[k] == 0.0)
// phase[k] = imx[k] < 0 ? Math.PI / 2 : -Math.PI / 2;
//else
phase[k] = Math.atan2(imx[k], rex[k]);
// unwrap the phase
if (k > 0) {
int c = (int) Math.round((uwphase[k-1] - phase[k]) / (2 * Math.PI));
uwphase[k] = phase[k] + c * 2 * Math.PI;
}
}
/*
// ignore 0, due to 1/f noise
for (int k = 1; k <= sampleSize / 2 ; k++) {
if (avgmag[k] >= maxMag / 2) {
int m;
m = (int)(avgmag[k] * avgmag.length / (avgmag.length / 2));
if (m != 0) {
int h = (int) (k * (55930. / 2) / avgmag.length);
System.out.println(h + " Hz: " + m);
}
}
}
*/
/*
for (int k = 1; k <= sampleSize / 2 ; k++) {
int h = (int) (k * (55930. / 2) / avgmag.length);
System.out.println(h + " Hz: " + uwphase[k]);
}
*/
}
}