Reputation: 88
I'm working on a project where I have to analyze .wav
files, which basically for me comes down to being able to display the frequencies that are present in said file.
I'm using the WavFile
class to read the file, and then I FFT them using the JTransforms
class (I actually do a realForward
since I'm feeding it real numbers only).
Everything seems to work fine, until I feed the data into Excel: I throw a line numeration in the first column (1-8000 for the file that I'm testing with) and I throw the output of the FFT into the next column. The file I'm feeding is a simple one-frequency sound, at 440Hz, with a duration of 1 second.
After seeing the graph, though, there's a problem: I have a single frequency peak, which is exactly what I'm expecting, but the peak is situated at position 880, which is double the actual frequency. Can somebody explain to me why is that?
Bonus question: Why am I getting values around e-16
? Everything other than the peak should be 0, right? (I fix this by writing a 0 to the file each time the data I get is <= 1
- see code below). Maybe it's "noise"?
There's two classes. The first one, readWav
is used to, well, read the .wav
file. The second one, wavFFT
, is the one who actually FFTs the data.
Code for readWav.java
:
import WavFile.*;
import java.io.*;
public class readWav {
// getter methods
public static long getWavFrames(File file)
{
// try loop to catch any exception
try {
// open the wav file
WavFile wavFile = WavFile.openWavFile(file);
// return the number of frames
return wavFile.getNumFrames();
} catch (Exception e) {
System.err.println(e);
// error value
return -1;
}
}
public static int getWavChannels(File file)
{
// try loop to catch any exception
try {
WavFile wavFile = WavFile.openWavFile(file);
return wavFile.getNumChannels();
} catch (Exception e) {
System.err.println(e);
// error value
return -1;
}
}
public static double[] getWavData(File file)
{
// try loop to catch any exception
try {
// open the file
WavFile wavFile = WavFile.openWavFile(file);
// use the getter method to get the channel number (should be mono)
int numChannels = getWavChannels(file);
// same, but with the frame getter method
int numFrames = (int) getWavFrames(file); // possible data loss
// create a buffer the size of the number of frames
double[] buffer = new double[numFrames * numChannels];
// Read frames into buffer
wavFile.readFrames(buffer, numFrames);
// Close the wavFile
wavFile.close();
return buffer;
} catch (Exception e) {
System.err.println(e);
// throw an error, if this runs something went wrong in reading the .wav file
throw new RuntimeException("[could not read wav file " + file + "]");
}
}
// main method, solely for testing purposes
public static void main(String[] args)
{
// test, everything seems to be working
File fichier_son = new File("son/freq1.wav");
double[] test = getWavData(fichier_son);
for(int i = 0; i<test.length; i++){
System.out.println(test[i]);
}
}
}
Code for wavFFT.java
:
import org.jtransforms.fft.DoubleFFT_1D;
import java.io.File;
import java.io.PrintWriter;
import java.io.IOException;
public class wavFFT {
public static double[] realFFT(File file)
{
// Get the .wav data using the readWav class
double[] data_to_fft = readWav.getWavData(file);
/* Get the length of the array.
Since we are feeding real numbers into the fft,
the length of the array should be equal to the
number of frames, which we get using the readWav class. */
int n = (int) readWav.getWavFrames(file);
// Make a new fft object
DoubleFFT_1D fft = new DoubleFFT_1D(n);
// Perform the realForward fft
fft.realForward(data_to_fft);
// Return the final data
return data_to_fft;
}
public static void writeToFile(File in, File out) throws IOException
{
PrintWriter print_out = new PrintWriter(out);
int i;
double[] data_to_file = realFFT(in);
for(i=0; i<data_to_file.length; i++){
if(data_to_file[i] > 1){
print_out.println(data_to_file[i]);
} else {
print_out.println(0);
}
}
print_out.close();
}
// main method, solely for testing purposes
public static void main(String[] args) {
File fichier_son = new File("son/freq1.wav");
double[] test = realFFT(fichier_son);
int i;
for(i=0; i<test.length; i++){
System.out.println(test[i]);
}
try{
writeToFile(fichier_son, new File("datafiles/output.txt"));
} catch (IOException e){
System.out.println("error");
}
}
}
Upvotes: 0
Views: 1959
Reputation: 9159
You don't say how you interpret the results in Excel that you generate with the code above. However, a likely error is misunderstanding the output of the FFTfft.realForward()
- which is an array of complex numbers, whose real and imaginary parts occupy consecutive elements, as documented here. If you simply use the index of the array in which the peak occurs, your result would be off by a factor of two. Note that this FFT implementation only calculates up to the Nyqvist rate (beyond this merely yields an 'alias').
Other things to note:
10e-16
. There are better choices the your application.Upvotes: 2