PDA

View Full Version : Improving Java Frequency Analysis


prrodigenius
10-05-2008, 11:24 PM
I am trying to make a program that tells me what pitch I am singing when I use a microphone input. The problem is, it runs very slowly. Do you know any tips I could use to speed it up?

I think it may be faster if I directly analyzed the sound to determine pitch. Currently I create a wave file and analyze that. How would I do it directly? Thanks so much!



import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import javax.sound.sampled.*;



public class Frequency4 {

private static final int EXTERNAL_BUFFER_SIZE = 1280000; // Space to store Sound

public void record()
{
byte[] voiceData = new byte[800];// Run time and accuracy appear to be dependent on this

// 250: completes 20 samples in 8.95 seconds
// 300: completes 20 samples in 9.40 seconds
try
{

AudioFormat linearFormat = new AudioFormat(1200,8,2,true,false);

DataLine.Info info = new DataLine.Info(TargetDataLine.class,linearFormat);
TargetDataLine targetDataLine = (TargetDataLine)AudioSystem.getLine(info);

targetDataLine.open(linearFormat);

targetDataLine.start();

AudioInputStream linearStream = new AudioInputStream(targetDataLine);

linearStream.read(voiceData,0,voiceData.length);

targetDataLine.stop();

targetDataLine.close();

File audioFile = new File("sample.wav");
ByteArrayInputStream baiStream = new ByteArrayInputStream(voiceData);
AudioInputStream aiStream = new AudioInputStream(baiStream,linearFormat,voiceData.length);
AudioSystem.write(aiStream,AudioFileFormat.Type.WAVE,audioFile);
aiStream.close();
baiStream.close();

}
catch(Exception e)
{
e.printStackTrace();
}
}

public static String returnNote(double highfrequency)
{
for (int L = 4; L >= 0; L--) // Translates frequency into a note
{
double P = Math.pow(2,L);
if (highfrequency > 95.25 * P) // Powers of 2 are equal to harmonic series
return "G";
else if (highfrequency > 89.905 * P)
return "F#";
else if (highfrequency > 84.86 * P)
return "F";
else if (highfrequency > 80.1 * P)
return "E";
else if (highfrequency > 75.6 * P)
return "D#";
else if (highfrequency > 71.5 * P)
return "D";
else if (highfrequency > 67.355 * P)
return "C#";
else if (highfrequency > 63.56 * P)
return "C";
else if (highfrequency > 60 * P)
return "B";
else if (highfrequency > 56.635 * P)
return "A#";
else if (highfrequency > 53.45 * P)
return "A";
else if (highfrequency > 50.44 * P)
return "G#";
}
return "Error";
}


public Frequency4()
{
record();
File soundFile = new File("sample.wav"); // Sound file that's analyzed
AudioInputStream audioInputStream = null;
try {
audioInputStream = AudioSystem.getAudioInputStream(soundFile); //Load the Audio Input Stream from the file
}
catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
AudioFormat audioFormat = audioInputStream.getFormat(); //Get Audio Format information
SourceDataLine line = null; //Handle opening the line
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
try {
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(audioFormat);
}
catch (LineUnavailableException e) {
e.printStackTrace();
System.exit(1);
}
catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
//line.start(); //Start playing the sound (doesn't need to play)
int nBytesRead = 0; //Write the sound to an array of bytes
byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];
while (nBytesRead != -1)
{
try {
nBytesRead = audioInputStream.read(abData, 0, abData.length);
}
catch (IOException e) {
e.printStackTrace();
}
if (nBytesRead >= 0) {
int nBytesWritten = line.write(abData, 0, nBytesRead);
}
}
line.drain(); //close the line
line.close();

float sample_rate = audioFormat.getSampleRate(); //Calculate the sample rate
//System.out.println("sample rate = "+sample_rate);
float T = audioInputStream.getFrameLength() / audioFormat.getFrameRate(); //Calculate the length in seconds of the sample
//System.out.println("T = "+T+ " (length of sampled sound in seconds)");
int n = (int) (T * sample_rate) / 2; //Calculate the number of equidistant points in time
//System.out.println("n = "+n+" (number of equidistant points)");
float h = (T / n); //Calculate the time interval at each equidistant point
//System.out.println("h = "+h+" (length of each time interval in seconds)");


boolean isBigEndian = audioFormat.isBigEndian(); //Determine the original Endian encoding format
int x[] = new int[n]; //this array is the value of the signal at time i*h
for (int i = 0; i < n*2; i+=2) //convert each pair of byte values from the byte array to an Endian value
{
int b1 = abData[i];
int b2 = abData[i + 1];
if (b1 < 0) b1 += 0x100;
if (b2 < 0) b2 += 0x100;
int value;
if (!isBigEndian) value = (b1 << 8) + b2; //Store the data based on the original Endian encoding format
else value = b1 + (b2 << 8);
x[i/2] = value;
}

double f[] = new double[n/2]; //do the DFT for each value of x sub j and store as f sub j
double highfrequency = 0; // Will store frequency at highest amplitude
double highamplitude = 0; // Will store highest amplitude
for (int j = 0; j < n/2; j++)
{
double firstSummation = 0;
double secondSummation = 0;
for (int k = 0; k < n; k++)
{
double twoPInjk = ((2 * Math.PI) / n) * (j * k); // Whole bunch of complicated math
firstSummation += x[k] * Math.cos(twoPInjk);
secondSummation += x[k] * Math.sin(twoPInjk);
}
f[j] = Math.abs( Math.sqrt(Math.pow(firstSummation,2) + Math.pow(secondSummation,2)) );
double amplitude = 2 * f[j]/n;
double frequency = j * h / T * sample_rate;
if (frequency > 100) // Added so number of printed lines isn't insane
{
if (frequency < 1000)
{
//System.out.println("frequency = "+frequency+", amp = "+amplitude);
if (amplitude > highamplitude) // Stores frequency and amplitude if new amplitude is higher than the record
{
highfrequency = frequency;
highamplitude = amplitude;
}
}
else
break; // If the frequency gets high, this breaks so it doesn't go on forever
}
}
System.out.println("High Frequency = "+highfrequency + " Amplutude = " + highamplitude);

System.out.println(returnNote(highfrequency));


}



public static void main(String[] args) {
for (int i = 0; i < 1000; i++){
new Frequency4();
}
}
}

shyam
10-06-2008, 06:27 PM
how big is the wav file? how long does it take for this particular wav file?

prrodigenius
10-07-2008, 07:32 AM
It's very small. It takes about half a second to run currently.

swolfram
11-03-2008, 01:56 AM
Is there a reason you're storing it to a wav file? You could save the wave file output to a ByteArrayOutputStream then convert it to a ByteArrayInputStream for the read? Also it looks like the read call will read until the buffer is full you could either decrease the size of voiceData or increase your sample rate?

jeff8j
12-21-2008, 09:40 PM
I am trying to output to a ByteArrayOutputStream then convert it to a ByteArrayInputStream like swolfram said. I have been going nuts doing this without success. Can someone show me how to do this please?