本文實例講述了java讀取wav文件(波形文件)并繪制波形圖的方法。分享給大家供大家參考。具體如下:
因為最近有不少網友詢問我波形文件讀寫方面的問題,出于讓大家更方便以及讓代碼能夠得到更好的改進,我將這部分(波形文件的讀寫)代碼開源在GitHub上面。
地址為https://github.com/sintrb/WaveAccess/,最新的代碼、例子、文檔都在那上面,我會在我時間精力允許的前提下對該項目進行維護,同時也希望對這方面有興趣的網友能夠加入到該開源項目上。
以下內容基本都過期了,你可以直接去GitHub上面閱讀、下載該項目。
因項目需要讀取.wav文件(波形文件)并繪制波形圖,因此簡單的做了這方面的封裝。
其實主要是對wav文件讀取的封裝,下面是一個wav文件讀取器的封裝:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
// 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); } // 判斷是否創建wav讀取器成功 public boolean isSuccess() { return issuccess; } // 獲取每個采樣的編碼長度,8bit或者16bit public int getBitPerSample(){ return this .bitspersample; } // 獲取采樣率 public long getSampleRate(){ return this .samplerate; } // 獲取聲道個數,1代表單聲道 2代表立體聲 public int getNumChannels(){ return this .numchannels; } // 獲取數據長度,也就是一共采樣多少個 public int getDataLen(){ return this .len; } // 獲取數據 // 數據是一個二維數組,[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教程而來的波形繪制面板:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
// 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 ; //上一個坐標 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 = 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; } } } |
有了這些之后就可以調用繪制了,簡單的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
// 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); // 創建一個繪制波形的面板 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文件" ); } } } |
工程的源代碼可以在我的百度網盤上找到,直接到開源JAVA
放上效果圖一張:
希望本文所述對大家的java程序設計有所幫助。