java讀取wav文件(波形文件)并繪制波形圖的方法
本文實例講述了java讀取wav文件(波形文件)并繪制波形圖的方法。分享給大家供大家參考。具體如下:
因為最近有不少網(wǎng)友詢問我波形文件讀寫方面的問題,出于讓大家更方便以及讓代碼能夠得到更好的改進(jìn),我將這部分(波形文件的讀寫)代碼開源在GitHub上面。
地址為https://github.com/sintrb/WaveAccess/,最新的代碼、例子、文檔都在那上面,我會在我時間精力允許的前提下對該項目進(jìn)行維護(hù),同時也希望對這方面有興趣的網(wǎng)友能夠加入到該開源項目上。
以下內(nèi)容基本都過期了,你可以直接去GitHub上面閱讀、下載該項目。
因項目需要讀取.wav文件(波形文件)并繪制波形圖,因此簡單的做了這方面的封裝。
其實主要是對wav文件讀取的封裝,下面是一個wav文件讀取器的封裝:
// filename: WaveFileReader.java
// RobinTang
// 2012-08-23
import java.io.*;
public class WaveFileReader {
private String filename = null;
private int[][] data = null;
private int len = 0;
private String chunkdescriptor = null;
static private int lenchunkdescriptor = 4;
private long chunksize = 0;
static private int lenchunksize = 4;
private String waveflag = null;
static private int lenwaveflag = 4;
private String fmtubchunk = null;
static private int lenfmtubchunk = 4;
private long subchunk1size = 0;
static private int lensubchunk1size = 4;
private int audioformat = 0;
static private int lenaudioformat = 2;
private int numchannels = 0;
static private int lennumchannels = 2;
private long samplerate = 0;
static private int lensamplerate = 2;
private long byterate = 0;
static private int lenbyterate = 4;
private int blockalign = 0;
static private int lenblockling = 2;
private int bitspersample = 0;
static private int lenbitspersample = 2;
private String datasubchunk = null;
static private int lendatasubchunk = 4;
private long subchunk2size = 0;
static private int lensubchunk2size = 4;
private FileInputStream fis = null;
private BufferedInputStream bis = null;
private boolean issuccess = false;
public WaveFileReader(String filename) {
this.initReader(filename);
}
// 判斷是否創(chuàng)建wav讀取器成功
public boolean isSuccess() {
return issuccess;
}
// 獲取每個采樣的編碼長度,8bit或者16bit
public int getBitPerSample(){
return this.bitspersample;
}
// 獲取采樣率
public long getSampleRate(){
return this.samplerate;
}
// 獲取聲道個數(shù),1代表單聲道 2代表立體聲
public int getNumChannels(){
return this.numchannels;
}
// 獲取數(shù)據(jù)長度,也就是一共采樣多少個
public int getDataLen(){
return this.len;
}
// 獲取數(shù)據(jù)
// 數(shù)據(jù)是一個二維數(shù)組,[n][m]代表第n個聲道的第m個采樣值
public int[][] getData(){
return this.data;
}
private void initReader(String filename){
this.filename = filename;
try {
fis = new FileInputStream(this.filename);
bis = new BufferedInputStream(fis);
this.chunkdescriptor = readString(lenchunkdescriptor);
if(!chunkdescriptor.endsWith("RIFF"))
throw new IllegalArgumentException("RIFF miss, " + filename + " is not a wave file.");
this.chunksize = readLong();
this.waveflag = readString(lenwaveflag);
if(!waveflag.endsWith("WAVE"))
throw new IllegalArgumentException("WAVE miss, " + filename + " is not a wave file.");
this.fmtubchunk = readString(lenfmtubchunk);
if(!fmtubchunk.endsWith("fmt "))
throw new IllegalArgumentException("fmt miss, " + filename + " is not a wave file.");
this.subchunk1size = readLong();
this.audioformat = readInt();
this.numchannels = readInt();
this.samplerate = readLong();
this.byterate = readLong();
this.blockalign = readInt();
this.bitspersample = readInt();
this.datasubchunk = readString(lendatasubchunk);
if(!datasubchunk.endsWith("data"))
throw new IllegalArgumentException("data miss, " + filename + " is not a wave file.");
this.subchunk2size = readLong();
this.len = (int)(this.subchunk2size/(this.bitspersample/8)/this.numchannels);
this.data = new int[this.numchannels][this.len];
for(int i=0; i<this.len; ++i){
for(int n=0; n<this.numchannels; ++n){
if(this.bitspersample == 8){
this.data[n][i] = bis.read();
}
else if(this.bitspersample == 16){
this.data[n][i] = this.readInt();
}
}
}
issuccess = true;
} catch (Exception e) {
e.printStackTrace();
}
finally{
try{
if(bis != null)
bis.close();
if(fis != null)
fis.close();
}
catch(Exception e1){
e1.printStackTrace();
}
}
}
private String readString(int len){
byte[] buf = new byte[len];
try {
if(bis.read(buf)!=len)
throw new IOException("no more data!!!");
} catch (IOException e) {
e.printStackTrace();
}
return new String(buf);
}
private int readInt(){
byte[] buf = new byte[2];
int res = 0;
try {
if(bis.read(buf)!=2)
throw new IOException("no more data!!!");
res = (buf[0]&0x000000FF) | (((int)buf[1])<<8);
} catch (IOException e) {
e.printStackTrace();
}
return res;
}
private long readLong(){
long res = 0;
try {
long[] l = new long[4];
for(int i=0; i<4; ++i){
l[i] = bis.read();
if(l[i]==-1){
throw new IOException("no more data!!!");
}
}
res = l[0] | (l[1]<<8) | (l[2]<<16) | (l[3]<<24);
} catch (IOException e) {
e.printStackTrace();
}
return res;
}
private byte[] readBytes(int len){
byte[] buf = new byte[len];
try {
if(bis.read(buf)!=len)
throw new IOException("no more data!!!");
} catch (IOException e) {
e.printStackTrace();
}
return buf;
}
}
為了繪制波形,因此做了一個從JPanel教程而來的波形繪制面板:
// filename: DrawPanel.java
// RobinTang
// 2012-08-23
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
@SuppressWarnings("serial")
public class DrawPanel extends JPanel {
private int[] data = null;
public DrawPanel(int[] data) {
this.data = data;
}
@Override
protected void paintComponent(Graphics g) {
int ww = getWidth();
int hh = getHeight();
g.setColor(Color.WHITE);
g.fillRect(0, 0, ww, hh);
int len = data.length;
int step = len/ww;
if(step==0)
step = 1;
int prex = 0, prey = 0; //上一個坐標(biāo)
int x = 0, y = 0;
g.setColor(Color.RED);
double k = hh/2.0/32768.0;
for(int i=0; i<ww; ++i){
x = i;
// 下面是個三點取出并繪制
// 實際中應(yīng)該按照采樣率來設(shè)置間隔
y = hh-(int)(data[i*3]*k+hh/2);
System.out.print(y);
System.out.print(" ");
if(i!=0){
g.drawLine(x, y, prex, prey);
}
prex = x;
prey = y;
}
}
}
有了這些之后就可以調(diào)用繪制了,簡單的:
// WaveFileReadDemo.java
// RobinTang
// 2012-08-23
import javax.swing.JFrame;
public class WaveFileReadDemo {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String filename = "file.wav";
JFrame frame = new JFrame();
WaveFileReader reader = new WaveFileReader(filename);
if(reader.isSuccess()){
int[] data = reader.getData()[0]; //獲取第一聲道
DrawPanel drawPanel = new DrawPanel(data); // 創(chuàng)建一個繪制波形的面板
frame.add(drawPanel);
frame.setTitle(filename);
frame.setSize(800, 400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
else{
System.err.println(filename + "不是一個正常的wav文件");
}
}
}
工程的源代碼可以在我的百度網(wǎng)盤上找到,直接到開源JAVA
放上效果圖一張:

希望本文所述對大家的java程序設(shè)計有所幫助。
相關(guān)文章
Java多線程 ReentrantReadWriteLock原理及實例詳解
這篇文章主要介紹了Java多線程 ReentrantReadWriteLock原理及實例詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-09-09
JPA中@CreatedDate和@LastModifiedDate的使用方式
這篇文章主要介紹了JPA中@CreatedDate和@LastModifiedDate的使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11
SpringCloud整合Nacos實現(xiàn)流程詳解
這篇文章主要介紹了SpringCloud整合Nacos實現(xiàn)流程詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-09-09
Java C++題解leetcode1620網(wǎng)絡(luò)信號最好的坐標(biāo)
這篇文章主要為大家介紹了Java C++題解leetcode1620網(wǎng)絡(luò)信號最好的坐標(biāo)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01

