java讀取wav文件(波形文件)并繪制波形圖的方法
本文實(shí)例講述了java讀取wav文件(波形文件)并繪制波形圖的方法。分享給大家供大家參考。具體如下:
因?yàn)樽罱胁簧倬W(wǎng)友詢問(wèn)我波形文件讀寫方面的問(wèn)題,出于讓大家更方便以及讓代碼能夠得到更好的改進(jìn),我將這部分(波形文件的讀寫)代碼開源在GitHub上面。
地址為https://github.com/sintrb/WaveAccess/,最新的代碼、例子、文檔都在那上面,我會(huì)在我時(shí)間精力允許的前提下對(duì)該項(xiàng)目進(jìn)行維護(hù),同時(shí)也希望對(duì)這方面有興趣的網(wǎng)友能夠加入到該開源項(xiàng)目上。
以下內(nèi)容基本都過(guò)期了,你可以直接去GitHub上面閱讀、下載該項(xiàng)目。
因項(xiàng)目需要讀取.wav文件(波形文件)并繪制波形圖,因此簡(jiǎn)單的做了這方面的封裝。
其實(shí)主要是對(duì)wav文件讀取的封裝,下面是一個(gè)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; } // 獲取每個(gè)采樣的編碼長(zhǎng)度,8bit或者16bit public int getBitPerSample(){ return this.bitspersample; } // 獲取采樣率 public long getSampleRate(){ return this.samplerate; } // 獲取聲道個(gè)數(shù),1代表單聲道 2代表立體聲 public int getNumChannels(){ return this.numchannels; } // 獲取數(shù)據(jù)長(zhǎng)度,也就是一共采樣多少個(gè) public int getDataLen(){ return this.len; } // 獲取數(shù)據(jù) // 數(shù)據(jù)是一個(gè)二維數(shù)組,[n][m]代表第n個(gè)聲道的第m個(gè)采樣值 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; } }
為了繪制波形,因此做了一個(gè)從JPanel教程而來(lái)的波形繪制面板:
// 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; //上一個(gè)坐標(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; // 下面是個(gè)三點(diǎn)取出并繪制 // 實(shí)際中應(yīng)該按照采樣率來(lái)設(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)用繪制了,簡(jiǎn)單的:
// 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)建一個(gè)繪制波形的面板 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 + "不是一個(gè)正常的wav文件"); } } }
工程的源代碼可以在我的百度網(wǎng)盤上找到,直接到開源JAVA
放上效果圖一張:
希望本文所述對(duì)大家的java程序設(shè)計(jì)有所幫助。
相關(guān)文章
Java實(shí)現(xiàn)堆排序(大根堆)的示例代碼
這篇文章主要介紹了Java實(shí)現(xiàn)堆排序(大根堆)的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10Java多線程 ReentrantReadWriteLock原理及實(shí)例詳解
這篇文章主要介紹了Java多線程 ReentrantReadWriteLock原理及實(shí)例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09JPA中@CreatedDate和@LastModifiedDate的使用方式
這篇文章主要介紹了JPA中@CreatedDate和@LastModifiedDate的使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11一問(wèn)詳解SpringBoot配置文件優(yōu)先級(jí)
在SpringBoot項(xiàng)目當(dāng)中,我們要想配置一個(gè)屬性,可以通過(guò)這三種方式當(dāng)中的任意一種來(lái)配置都可以,那么優(yōu)先級(jí)怎么算,本文主要介紹了一問(wèn)詳解SpringBoot配置文件優(yōu)先級(jí),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04SpringCloud整合Nacos實(shí)現(xiàn)流程詳解
這篇文章主要介紹了SpringCloud整合Nacos實(shí)現(xiàn)流程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09Java C++題解leetcode1620網(wǎng)絡(luò)信號(hào)最好的坐標(biāo)
這篇文章主要為大家介紹了Java C++題解leetcode1620網(wǎng)絡(luò)信號(hào)最好的坐標(biāo)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01