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();
}
}
}
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();
}
}
}