搜尋此網誌

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


Java 用麥克風錄音

===========================只講解主要程式=============================
public void main() throws InterruptedException{
//設定錄完音之後的檔名以及路徑
String Filename = "C://JDKAudioRecord.wav";
File outputFile = new File(Filename);

AudioFormat audioFormat = null;
//設定音源檔的格式
//詳細api請看這 Java sound api
audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
44100.0F, 16, 2, 4, 44100.0F, false);
//DataLine.Info subclass 去取得並且開啟 target data line
DataLine.Info info = new DataLine.Info(TargetDataLine.class,
audioFormat);
TargetDataLine targetDataLine = null;
try{
//從麥克風取得音源
targetDataLine = (TargetDataLine)AudioSystem.getLine(info);
targetDataLine.open(audioFormat);
}catch (Exception e){
System.out.println("無法錄音,錄音失敗");
e.printStackTrace();
System.exit(-1);
}

2009年10月17日 星期六

java-呼叫不同calss的method

由於目前寫得程式 我都把它包在同一個class下去執行 這次我把我要的功能分成兩個class 看起來比較明瞭簡單 順便學習一下
EX:
Server2.java (主要執行的程式)
JDKAudioRecorder.java (執行錄音的程式)


我在server2.java中 加入一段
JDKAudioRecorder s = new JDKAudioRecorder(targetDataLine,targetType,outputFile);
由於在 JDKAudioRecorder的constructor是有引數的 所以在這之前需要事先定義引數
如下:
AudioFormat audioFormat = null;
audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,44100.0F, 16, 2, 4, 44100.0F, false);
TargetDataLine targetDataLine = null;
AudioFileFormat.Type targetType = AudioFileFormat.Type.AU;
DataLine.Info info = new DataLine.Info(TargetDataLine.class,audioFormat);
targetDataLine = (TargetDataLine)AudioSystem.getLine(info);
targetDataLine.open(audioFormat);
String Filename = "C://JDKAudioRecord.wav";
File outputFile = new File(Filename);

接著我只要在server2.java中 加入s.main() (main method裡面就能起動整個JDKAudioRecorder.java 要做的事) 因此加入這段就等於把整個JDKAudioRecorder.java執行一次的意思一樣

2009年10月13日 星期二

暫存

最近在處理對聲音判斷其音量大小 目前有點卡關 暫存一些我查到的資訊
似乎是可以利用sample size 和number of channels 來決定此音源的音量大小

Digital audio is stored as a series of samples, where each sample is the amplitude of the sound wave being recorded. The volume of the sound is the same as the amplitude of the sound, therefore, each individual sample is simply encoded as the volume of the sound.

So you break the mic input down into a list of samples (based on the sample size / number of channels) and keep a running average to determine the sound level.

2009年10月7日 星期三

論文後續

情境感知:

(1)被動:
server可以透過google calendar(或其他行事曆的方式),也就是Location+Time,讓server瞭解它所在的地方,是在哪一種情境
ps:但也有可能預定了某個地方是在開會,但卻突然更動,沒有開會,這樣server還能夠用主動的方式,來瞭解它所在的地方是哪種情境

(2)主動:
server可以像人般(有五官)的瞭解它所在的情境在哪
ex:可透過聲音(聲音是否大聲),攝影機(看這個地方人多不多)等

方法:
其他文獻提到,我實驗所歸納的,幾個常見的情境
接著用五官,去分析這幾個情境,看是否能夠分離出來

ex:聲音小聲=>開會、電影院等
聲音大聲=>下課時間等

不知道這樣半年能不能寫的完@@ 囧....