Hello Javax.Sound users. I hope somebody can help. I've written a class based on JLayer following the model
here . I want to analyze the peak values in a sound file. Everything seems to work and the milliseconds work out, but a quick look in Audacity reveals that I'm doing something horribly wrong. The peak data have very little to do with the correct data (and graphing it looks even more random). Here's the code. Thanks in advance to everyone for your help.
package whatAmIDoingWrong;
import java.io.File;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
public class AnalyzeMp3 {
/**
* @param args
*/
public static void main(String[] args) throws Exception {
new AnalyzeMp3("D:/music/Learning Rhythms/Las Claves/20 120 - 120.mp3");
}
private int lastByteProcessed = 0;
private double bytesPerFrame = 0;
private double framesPerMillisecond = 0;
private double getLastByteProcessedInMilliseconds() {
double retVal = lastByteProcessed/bytesPerFrame*framesPerMillisecond;
return retVal;
}
public AnalyzeMp3(String filename) throws Exception
{
File file = new File(
filename);
if (!file.exists()) {
System.out.println("File not found, sorry");
return;
}
AudioInputStream in = AudioSystem.getAudioInputStream(file);
AudioInputStream din = null;
AudioFormat baseFormat = in.getFormat();
AudioFormat decodedFormat = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED, baseFormat.getSampleRate(),
16, baseFormat.getChannels(), baseFormat.getChannels() * 2,
baseFormat.getSampleRate(), false);
din = AudioSystem.getAudioInputStream(decodedFormat, in);
// members
bytesPerFrame =
decodedFormat.getSampleSizeInBits()/8*decodedFormat.getChannels();
framesPerMillisecond = 1000/decodedFormat.getSampleRate();
// Play it or analyze it
int length = playIt(decodedFormat, din, false);
System.out.println("samples=" + length/bytesPerFrame +
" so total seconds=" +
(float)length/bytesPerFrame/decodedFormat.getSampleRate());
in.close();
}
/* returns the number of samples just to make sure we're on track */
private int playIt(AudioFormat targetFormat, AudioInputStream din, boolean playAlso) throws Exception {
SourceDataLine line = null;
if (playAlso) {
line = getLine(targetFormat);
line.start();
}
int retVal = 0;
byte[] data = new byte[1024];
int nBytesRead = 0;
while (nBytesRead != -1) {
nBytesRead = din.read(data, 0, data.length);
if (nBytesRead != -1) {
retVal += nBytesRead;
if (playAlso) {
line.write(data, 0, nBytesRead);
}
process(data, targetFormat.getChannels());
}
}
if (playAlso) {
line.drain();
line.stop();
line.close();
}
din.close();
return retVal;
}
private static SourceDataLine getLine(AudioFormat audioFormat) throws Exception {
SourceDataLine res = null;
DataLine.Info info = new DataLine.Info(SourceDataLine.class,
audioFormat);
res = (SourceDataLine) AudioSystem.getLine(info);
res.open(audioFormat);
return res;
}
public void process(byte[] data, int numOfChannels) {
int numOfSamples = data.length / 2; // two bytes per sample
numOfSamples = numOfSamples / numOfChannels; // two samples, one
// for each channel
int offset = 0;
// init the max values
int[] maxValue = new int[numOfChannels]; // one max per channel
for (int channel=0; channel<numOfChannels;channel++) {
maxValue[channel]=0;
}
// run through the frames, analyzing each one
for (int i = 0; i < numOfSamples; i++) {
// run through the channels which gives us the sample
for (int channel=0; channel<numOfChannels; channel++) {
// we take the short and strip the sign to get the volume data 0-32K
int sample = Math.abs(arr2int(data, offset, false));
offset = offset + 2;
lastByteProcessed = lastByteProcessed + 2;
if (sample > maxValue[channel])
maxValue[channel] = sample;
}
}
System.out.print("Peak ms=" + (int)getLastByteProcessedInMilliseconds());
for (int channel=0; channel<numOfChannels;channel++) {
int peak = maxValue[channel];
System.out.print(", peak=" + Math.round(getDbValue(peak)));
}
System.out.println();
}
public static short arr2int(byte[] buffer, int offset, boolean bigEndian) {
short sample;
if (bigEndian) {
sample = (short)
( (buffer[offset + 0] << 8)
| (buffer[offset + 1] & 0xFF) );
} else {
sample = (short)
( (buffer[offset + 0] & 0xFF)
| (buffer[offset + 1] << 8) );
}
return sample;
}
public static double getDbValue(int peak) {
if (peak == 0)
return 0;
else return 20 * Math.log10((double)peak/ 32768) + 96;
}
}