Java实现一个简单的音频录音和播放功能,使用Swing创建图形用户界面,利用Java Sound API进行音频处理。下面是对此程序的详细剖析:
一、程序结构
程序主要由以下几个部分组成:
-
RecorderFrm类:主框架类,继承自JFrame,负责UI界面和主要逻辑
-
Recorder内部类:实现Runnable接口,负责录音功能
-
多个辅助方法:如getAudioFormat(), playMusic(), save(), open()等
二、程序主要功能
2.1 录音功能
- 使用TargetDataLine从麦克风捕获音频数据
- 音频数据存储在ByteArrayOutputStream中
- 录音过程在单独线程中运行,避免阻塞UI
2.2 播放功能
可以选择并播放已保存的WAV文件;使用SourceDataLine进行音频播放。
2.3 文件操作
-
可以将录音保存为WAV格式文件
-
可以打开已有的WAV文件进行播放
- 关键组件分析
3.1 音频格式设置
getAudioFormat()方法定义了音频参数:
编码:PCM_SIGNED
采样率:44100Hz
采样精度:16位
单声道(声道数:1)
帧大小:2字节
帧率:44100
字节序:小端(Little-endian)
3.2 录音流程
创建TargetDataLine
打开并启动数据线
循环读取音频数据到缓冲区
将数据写入ByteArrayOutputStream
停止时关闭资源
3.3 播放流程
选择音频文件
创建AudioInputStream
获取音频格式
创建并打开SourceDataLine
从输入流读取数据并写入数据线播放
3.4 文件保存
将ByteArrayOutputStream转换为ByteArrayInputStream
创建AudioInputStream
使用AudioSystem.write()方法将音频写入WAV文件

Java实现音频录音和播放机功能程序的源码:
cpp
package Record;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.sound.sampled.TargetDataLine;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
public class RecorderFrm extends JFrame implements ActionListener {
//队列queue,放置声道音频数据。录音用一个话筒,单声道。
Queue<Short> qAudio = new LinkedList<>();
//定义字节数组输出流。 用于保存音频数据。
ByteArrayOutputStream bOutStm = null;
Boolean stopflag = false; //停止录音的标志,用于控制录音线程的运行
//定义所需要的组件
JPanel panelA, panelB;
JButton recordeBtn, stopBtn, playBtn;
//构造函数
public RecorderFrm() {
//面板组件初始化
panelA = new JPanel();
panelB = new JPanel();
//面板panelB上按钮组件的设置
recordeBtn = new JButton("开始录音");
recordeBtn.addActionListener(this); //给按钮注册监听
recordeBtn.setActionCommand("recorde");
stopBtn = new JButton("停止录音");
stopBtn.addActionListener(this); //给按钮注册监听
stopBtn.setActionCommand("stop");
playBtn = new JButton("播放录音");
playBtn.addActionListener(this); //给按钮注册监听
playBtn.setActionCommand("play");
//设置面板panelB
panelB.setLayout(null);
panelB.setLayout(new GridLayout(1, 4, 10, 10));
panelB.add(recordeBtn);
panelB.add(stopBtn);
panelB.add(playBtn);
//设置按钮的初始属性
recordeBtn.setEnabled(true);
stopBtn.setEnabled(false);
playBtn.setEnabled(false);
//在主窗体上布置面板组件
add(panelA, BorderLayout.CENTER);
add(panelB, BorderLayout.SOUTH);
//设置窗口的属性
setSize(400, 160);
setTitle("音频录音播放机");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("recorde")) {
//点击录音按钮后的动作
recordeBtn.setEnabled(false);
stopBtn.setEnabled(true); //启用停止按钮
playBtn.setEnabled(false);
recorde(); //调用录音的方法
} else if (e.getActionCommand().equals("stop")) {
//点击停止录音按钮的动作
recordeBtn.setEnabled(true);
stopBtn.setEnabled(false);
playBtn.setEnabled(true);
stop(); //调用停止录音的方法
} else if (e.getActionCommand().equals("play")) {
play(); //调用播放录音的方法
}
}
//开始录音
public void recorde() {
//创建播放录音的线程
Recorder recorder = new Recorder();
Thread recorderTrd = new Thread(recorder);
recorderTrd.start();
}
//停止录音。设置"停止"标志,并保存录音
public void stop() {
stopflag = true;
save(); //保存音频文件到磁盘
}
//播放录音
public void play() {
File file = open(); //选择音频文件
System.out.println("正在播放录音... ");
playMusic(file); //打开音频文件播放音乐
}
//保存录音到磁盘
public void save() {
File file = null; //文件名
FileNameExtensionFilter filter; //后缀名过滤器
JFileChooser chooser;
//创建文件选择器,指定当前目录为"D:\test"。
chooser = new JFileChooser("D:\\test");
//文件后缀名过滤器
filter = new FileNameExtensionFilter("音频文件(*.wav,*.WAV)","wav","WAV");
chooser.setFileFilter(filter); //设置文件扩展名
ByteArrayInputStream bais = null; //定义字节数组输入流
AudioInputStream ais = null; //定义音频输入流
AudioFormat fmt = getAudioFormat(); //取得录音格式
byte audioData[] = bOutStm.toByteArray();
bais = new ByteArrayInputStream(audioData);
long len = audioData.length / fmt.getFrameSize();
ais = new AudioInputStream(bais,fmt,len);
//写入文件
try {
int value=chooser.showDialog(this,"保存文件");
if (value==JFileChooser.APPROVE_OPTION) { //判断用户是否保存文件
/* 使用文件句柄(对象),接收返回值。保存文件通常只需处理单选 ***/
file = chooser.getSelectedFile();
String pathStr = file.getAbsolutePath();
/***检查path中是否已包含文件扩展名***/
boolean flag = false;
for (String extension : filter.getExtensions()) {
if (pathStr.contains(extension)) {
flag=true;
break;
}
}
if (!flag) {
pathStr += ".wav"; //若不包含扩展名,添加扩展名
file = new File(pathStr); //重新创建文件句柄
}
System.out.println("保存文件的绝对路径: "+pathStr);
//保存文件操作
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, file);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭流
try {
if (bais != null) bais.close();
if (ais != null) ais.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//打开,选择录音文件
private File open() {
File path = null;
//设置文件过滤器
FileNameExtensionFilter filter = null;
filter = new FileNameExtensionFilter("音频文件(*.wav)","wav");
JFileChooser chooser = new JFileChooser("D:\\test");//默认路径
chooser.setFileFilter(filter);
chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
chooser.setMultiSelectionEnabled(false); //单选文件
int result = chooser.showDialog(this, "打开音频文件");
if (result==JFileChooser.APPROVE_OPTION) {
path = chooser.getSelectedFile();
}
return path;
}
public static void main(String[] args) {
new RecorderFrm(); //创建实例
}
private void playMusic(File path){
AudioInputStream inStream;
AudioFormat format;
SourceDataLine sourceDataLine;
DataLine.Info info;
try{
int count;
byte buf[] = new byte[1024];
//获取音频输入流
inStream = AudioSystem.getAudioInputStream(path);
//获取音频的编码格式
format = inStream.getFormat();
info = new DataLine.Info(SourceDataLine.class,
format,AudioSystem.NOT_SPECIFIED);
sourceDataLine = (SourceDataLine)AudioSystem.getLine(info);
sourceDataLine.open(format);
sourceDataLine.start();
//播放音频
while((count = inStream.read(buf,0,buf.length)) != -1){
sourceDataLine.write(buf,0,count);
}
//播放结束,释放资源
sourceDataLine.drain();
sourceDataLine.close();
inStream.close();
}catch(UnsupportedAudioFileException ex){
ex.printStackTrace();
}catch(LineUnavailableException ex){
ex.printStackTrace();
}catch(IOException ex){
ex.printStackTrace();
}
}
//设置AudioFormat的参数
public AudioFormat getAudioFormat() {
//参数Encoding:音频编码格式
AudioFormat.Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
/***其它参数说明
float sampleRate = 44100f; //采样率
int sampleSize = 16; //SampleSizeInBits: 采样精度
int channels = 2; //声道数
//FrameSize: 帧大小,每帧的字节数。
int frameSize=4;//frameSize=(sampleSize/8)*channels
float frameRate = 44100f; //FrameRate: 帧频,每秒的帧数。
boolean bigEndian = false;
* BigEndian: 与传输和存储相关的字节序列标志。
* 有两种类型:Big-endian和Little-endian
* 值false表示Little-endian;值true表示Big-endian。
* ***/
//AudioFormat(encoding,sampleRate,sampleSizeInBits,channels,frameSize,frameRate,bigEndian)
//AudioFormat format = new AudioFormat(encoding, 44100, 16, 2, 4, 44100, false); 立体声
AudioFormat format = new AudioFormat(encoding, 44100, 16, 1, 2, 44100, false);//单声道
return format;
}
//录音类,因为要用到主类中的变量,所以将其做成内部类
class Recorder implements Runnable {
DataLine.Info info = null;
AudioFormat format = null; //定义音频格式
//定义目标数据行,可以从中读取音频数据,该 TargetDataLine 接口提供从目标数据行的缓冲区读取所捕获数据的方法。
TargetDataLine tdLine = null;
//定义存放录音的字节数组,作为缓冲区
byte bts[] = new byte[2048];
//将字节数组包装到流里,最终存入到bOutStm中
@Override //重写run函数
public void run() {
bOutStm = new ByteArrayOutputStream();
try {
format = getAudioFormat();
info = new DataLine.Info(TargetDataLine.class, format);
tdLine = (TargetDataLine) (AudioSystem.getLine(info));
//打开具有指定格式的行,这样可使行获得所有所需的系统资源并变得可操作。
tdLine.open(format);
//允许某一数据行执行数据 I/O
tdLine.start();
/******/
System.out.println("正在录音...");
stopflag = false;
int size = format.getFrameSize();
while (stopflag != true) {
//当停止录音按钮未按下时,该线程一直执行
//从目标数据行读取音频数据到字节数组bts,cnt 是实际读取的字节数
int cnt = tdLine.read(bts, 0, bts.length);
if (cnt > 0) {
bOutStm.write(bts, 0, cnt);
}
}
System.out.println("录音结束!");
} catch (Exception e) {
e.printStackTrace();
} finally {
try { //关闭打开的字节数组流
if (bOutStm != null) bOutStm.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
tdLine.drain();
tdLine.close();
}
}
}
}
}
总结 本例程的技术要点:
-
使用Java Sound API进行底层音频操作
-
多线程处理录音过程
-
Swing构建用户界面
-
字节流处理音频数据
-
文件对话框进行文件操作
这个程序很好地演示了Java中音频录制和播放的基本原理,代码结构清晰,适合作为学习Java音频处理的示例。