搜尋此網誌

2009年11月9日 星期一

Java 計算音源檔音量

我想這應該是很多寫這方面程式的人 都想知道的 由於java 的getlevel有bug 因此沒辦法直接獲取音源的振幅,所以我們必須先瞭解 音源檔的結構 是怎麼組成 必須先繞個彎去計算出振幅 我也是找了好久才找到的 稍微修改一下 就能正常運作 但注意這程式碼是計算出振幅 不是分貝 所以只要找到怎麼將振幅轉換成分貝 加入一行公式 就可以計算出來(目前先停擺..我在寫paper 等有空我再補充進去)
以下是程式碼

public class PCMFilePlayerLeveler implements Runnable {
File file;
AudioInputStream in;
SourceDataLine line;
int frameSize;
byte[] buffer;
Thread playThread;
boolean playing;
boolean notYetEOF;
AudioFormat format;
float level;

final static float MAX_8_BITS_SIGNED = Byte.MAX_VALUE;
final static float MAX_8_BITS_UNSIGNED = 0xff;
final static float MAX_16_BITS_SIGNED = Short.MAX_VALUE;
final static float MAX_16_BITS_UNSIGNED = 0xffff;

public PCMFilePlayerLeveler ()
throws IOException,
UnsupportedAudioFileException,
LineUnavailableException {
File fi= new File("c:\\JDKAudioRecord.wav");
in = AudioSystem.getAudioInputStream (fi);
//in = AudioSystem.getAudioInputStream (new BufferedInputStream (new FileInputStream(f)));
format = in.getFormat();
AudioFormat.Encoding formatEncoding = format.getEncoding();
if (! (formatEncoding.equals (AudioFormat.Encoding.PCM_SIGNED) ||
formatEncoding.equals (AudioFormat.Encoding.PCM_UNSIGNED)))
throw new UnsupportedAudioFileException (
file.getName() + " is not PCM audio");
System.out.println ("got PCM format: " +
format.getChannels() + " channels, " +
format.getSampleSizeInBits() + " bit samples");
frameSize = format.getFrameSize();
System.out.println ("got frame size: ");
DataLine.Info info =
new DataLine.Info (SourceDataLine.class, format);
System.out.println ("got info");
line = (SourceDataLine) AudioSystem.getLine (info);

// figure out a small buffer size
int bytesPerSec = format.getSampleSizeInBits() *
(int) format.getSampleRate();
System.out.println ("bytesPerSec = " + bytesPerSec);
int bufferSize = bytesPerSec / 20;
buffer = new byte[bufferSize];

System.out.println ("got line");
//line.open();
System.out.println ("opened line");
playThread = new Thread (this);
playing = false;
notYetEOF = true;
playThread.start();
}

public void run() {
int readPoint = 0;
int bytesRead = 0;
try {
// while (notYetEOF) {
System.out.println("test");
//if (playing) {
// only write if the line will take at
// least a buffer-ful of data
if (line.available() < buffer.length) {
Thread.yield();
// continue;
}
bytesRead = in.read (buffer,
readPoint,
buffer.length - readPoint);
if (bytesRead == -1) {
notYetEOF = false;
// break;
}
// how many frames did we get,
// and how many are left over?
System.out.println("test");
int frames = bytesRead / frameSize;
int leftover = bytesRead % frameSize;
// calculate level
calculateLevel (buffer, readPoint, leftover);
// if (level > 1)
// System.out.println ("WTF? level = " + level);
// System.out.println ("level: " + level);
// send to line
line.write (buffer, readPoint, bytesRead-leftover);
// save the leftover bytes
System.arraycopy (buffer, bytesRead,
buffer, 0,
leftover);
readPoint = leftover;

/* } else {
// if not playing
// Thread.yield();
try { Thread.sleep (10);}
catch (InterruptedException ie) {}
}*/
// } // while notYetEOF
System.out.println ("reached eof");
line.drain();
line.stop();
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
// line.close();
}
} // run

/** resets level by finding max value in buffer, taking
into account whether these are 8 or 16 bit values
(doesn't care about mono vs stereo - if one channel
is disproportionately louder than the other, it wins)
*/
private void calculateLevel (byte[] buffer,
int readPoint,
int leftOver) {
int max = 0;
boolean use16Bit = (format.getSampleSizeInBits() == 16);
boolean signed = (format.getEncoding() ==
AudioFormat.Encoding.PCM_SIGNED);
boolean bigEndian = (format.isBigEndian());
if (use16Bit) {
for (int i=readPoint; i int value = 0;
// deal with endianness
int hiByte = (bigEndian ? buffer[i] : buffer[i+1]);
int loByte = (bigEndian ? buffer[i+1] : buffer [i]);
if (signed) {
short shortVal = (short) hiByte;
shortVal = (short) ((shortVal << 8) | (byte) loByte);
value = shortVal;
} else {
value = (hiByte << 8) | loByte;
}
max = Math.max (max, value);
} // for
} else {
// 8 bit - no endianness issues, just sign
for (int i=readPoint; i int value = 0;
if (signed) {
value = buffer [i];
} else {
short shortVal = 0;
shortVal = (short) (shortVal | buffer [i]);
value = shortVal;
}
max = Math.max (max, value);
} // for
} // 8 bit
// express max as float of 0.0 to 1.0 of max value
// of 8 or 16 bits (signed or unsigned)
if (signed) {
if (use16Bit) { level = (float) max / MAX_16_BITS_SIGNED; }
else { level = (float) max / MAX_8_BITS_SIGNED; }
} else {
if (use16Bit) { level = (float) max / MAX_16_BITS_UNSIGNED; }
else { level = (float) max / MAX_8_BITS_UNSIGNED; }
}
System.out.println(level);
} // calculateLevel





public void start() {
playing = true;
if (! playThread.isAlive())
playThread.start();
line.start();
}

public void stop() {
playing = false;
line.stop();
}

public SourceDataLine getLine() {
return line;
}

public File getFile() {
return file;
}

public float getLevel() {
return level;
}
public static void main(String args[]) throws IOException, UnsupportedAudioFileException, LineUnavailableException
{
PCMFilePlayerLeveler calculate= new PCMFilePlayerLeveler();
calculate.start();
//System.out.println("無法錄音,錄音失敗");
//System.out.println(calculate.getLevel());
}
}


沒有留言:

張貼留言