java 读取.wav文件(波形文件)并绘制波形图例子

因为最近有不少网友询问我波形文件读写方面的问题,出于让大家更方便以及让代码能够得到更好的改进,我将这部分(波形文件的读写)代码开源在GitHub上面。

地址为https://github.com/sintrb/WaveAccess/,最新的代码、例子、文档都在那上面,我会在我时间精力允许的前提下对该项目进行维护,同时也希望对这方面有兴趣的网友能够加入到该开源项目上。

以下内容基本都过期了,你可以直接去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);
	}
	
	// 判断是否创建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教程而来的波形绘制面板:

// 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;
		}
	}
}

有了这些之后就可以调用绘制了,简单的:

// WaveFileReadDemo.jave
// 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

放上效果图一张:



  • 11
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 30
    评论
要用Java绘制一个.wav音频文件的时域图,可以按照以下步骤进行: 1. 读取.wav文件,获取其中的音频数据。 2. 将音频数据转换为可绘制的格式,比如BufferedImage。 3. 绘制时域图,将转换后的音频数据按照时间序列进行绘制,形成一条连续的波形线。 下面是一个用Java绘制.wav音频文件时域图的示例代码: ```java import java.awt.Color; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.UnsupportedAudioFileException; public class WaveformGenerator { public static void main(String[] args) { // 指定.wav文件路径 String filePath = "test.wav"; // 读取.wav文件,获取音频数据 try (AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(filePath))) { int bytesPerFrame = audioInputStream.getFormat().getFrameSize(); int numBytes = (int) (audioInputStream.getFrameLength() * bytesPerFrame); byte[] audioBytes = new byte[numBytes]; int numBytesRead = 0; while (numBytesRead < audioBytes.length) { int numBytesRemaining = audioBytes.length - numBytesRead; int numBytesToRead = audioInputStream.read(audioBytes, numBytesRead, numBytesRemaining); if (numBytesToRead == -1) { break; } numBytesRead += numBytesToRead; } // 将音频数据转换为可绘制的格式 int width = 1000; // 绘制区域宽度 int height = 500; // 绘制区域高度 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics g = image.getGraphics(); g.setColor(Color.WHITE); g.fillRect(0, 0, width, height); int numSamples = numBytes / bytesPerFrame; int samplesPerPixel = numSamples / width; int numPixels = numSamples / samplesPerPixel; int yCenter = height / 2; int oldX = 0, oldY = yCenter; g.setColor(Color.BLACK); // 绘制时域图 for (int i = 1; i < numPixels; i++) { int sum = 0; for (int j = 0; j < samplesPerPixel; j++) { int index = (i - 1) * samplesPerPixel + j; int sample = 0; if (bytesPerFrame == 1) { sample = audioBytes[index]; } else if (bytesPerFrame == 2) { sample = audioBytes[index * 2] + (audioBytes[index * 2 + 1] << 8); } sum += sample; } int x = i; int y = yCenter + (sum / samplesPerPixel) / 65536; g.drawLine(oldX, oldY, x, y); oldX = x; oldY = y; } // 将绘制结果保存为图片文件 File outputfile = new File("waveform.png"); ImageIO.write(image, "png", outputfile); } catch (UnsupportedAudioFileException | IOException e) { e.printStackTrace(); } } } ``` 需要注意的是,上述代码只适用于采样精度为8或16位、单声道的.wav文件。如果需要处理其他类型的音频文件,需要对代码进行相应的修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RobinTang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值