java实现录音功能
java1234
共 22814字,需浏览 46分钟
· 2021-04-29
点击上方蓝色字体,选择“标星公众号”
优质文章,第一时间送达
前几次博客中也是用java实现录音机的各种功能,这次是比先前有了一点优化,将录音的功能单独提取出来,这个程序的实现用到了编码器LAME.exe这个的作用是将录音得到的wav文件编码成MP3文件,这样的结果是容量大大减少,因为LAME编码器经测试在编码过程中路径中带有空格问题未能解决,所以只能找了一个笨的方法,为了将其放到指定的文件夹下(这个文件夹路径是带有空格的),所以只能先存放到D盘,然后利用文件操作将其复制到指定的路径下,然后再删除,程序中还用到了图片,修改了java的图标,所以在运行时可将其注释掉,写的很烂,相互学习哈!
/*
* 实现录音机的功能
* 1.在原来的基础上界面更加精简,字体变大使用户更加方便
* 2.在停止录音后便直接保存为.mp3文件
* 3.对停止录音进行改进,将录音按钮设计成类似微信的形式,按下时进行录音,松开即停止录音并保存即可
* 4.在录音的时候在面板上显示音频波,表示正在录音
*/
package com.liuyun.MyRecord6;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.io.*;
import javax.sound.sampled.*;
import java.lang.*;
public class MyRecord6 extends JFrame implements MouseListener{
//定义录音格式
AudioFormat af = null;
//定义目标数据行,可以从中读取音频数据,该 TargetDataLine 接口提供从目标数据行的缓冲区读取所捕获数据的方法。
TargetDataLine td = null;
//定义源数据行,源数据行是可以写入数据的数据行。它充当其混频器的源。应用程序将音频字节写入源数据行,这样可处理字节缓冲并将它们传递给混频器。
SourceDataLine sd = null;
//定义字节数组输入输出流
ByteArrayInputStream bais = null;
ByteArrayOutputStream baos = null;
//定义音频输入流
AudioInputStream ais = null;
//定义停止录音的标志,来控制录音线程的运行
Boolean stopflag = false;
//记录开始录音的时间
long startPlay;
//设置一个播放的标志
Boolean playflag;
//每次保存的最后的文件名
File tarFile = null;
//定义音频波形每次显示的字节数
int intBytes = 0;
//定义每次录音的时候每次提取字节来画音频波
byte audioDataBuffer[] = null;
//定义所需要的组件
JPanel jp1,jp2,jp3;
JLabel jl1=null;
JButton captureBtn;
//设置画波形线程的终止的标志
boolean flag = true;
//定义播放录音时的一个计数值
int cnt;
//定义播放录音时一个缓冲数组
byte btsPlay[] = null;
int gridx, gridy, gridwidth, gridheight, anchor, fill, ipadx, ipady;
double weightx, weighty;
Insets inset;
GridBagConstraints c;
public static void main(String[] args) {
//创造一个实例
MyRecord6 mr = new MyRecord6();
}
//构造函数
public MyRecord6()
{
//组件初始化
jp1 = new JPanel();
jp2 = new JPanel();
jp3 = new JPanel();
//定义jp1的字体
Font jpFont = new Font("华文新魏",Font.BOLD,40);
jl1 = new JLabel("请留下您想说的话");
jl1.setFont(jpFont);
jl1.setForeground(Color.red);
jp1.add(jl1);
//定义按钮上面的字体
Font btFont = new Font("华文新魏",Font.BOLD,40);
captureBtn = new JButton("按住 说话");
//setForeground可以设置按钮上面字体的颜色
captureBtn.setForeground(Color.RED);
captureBtn.setFont(btFont);
//对开始录音按钮进行鼠标监听
captureBtn.addMouseListener(this);
this.add(jp1,BorderLayout.NORTH);
this.add(jp2,BorderLayout.CENTER);
this.add(jp3,BorderLayout.SOUTH);
GridBagLayout gridbag = null;
jp3.setLayout(gridbag = new GridBagLayout());
gridx=1;
gridy=2;
gridwidth=1;
gridheight=1;
weightx=1;
weighty=1;
anchor=GridBagConstraints.CENTER;
fill=GridBagConstraints.HORIZONTAL;
inset=new Insets(1,1,1,1);
ipadx=0;
ipady=30;
c = new GridBagConstraints(gridx, gridy, gridwidth, gridheight,
weightx, weighty, anchor, fill, inset, ipadx, ipady);
gridbag.setConstraints(captureBtn, c);
jp3.add(captureBtn);
//设置窗口的属性
this.setSize(800,500);
this.setTitle("录音机");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//设置窗口居中
this.setLocationRelativeTo(null);
//将窗口的边框去掉
this.setUndecorated(true);
this.setVisible(true);
//设置窗口上的图标
Image img = this.getToolkit().getImage(getClass().getResource("/image/Recorder.jpg"));
this.setIconImage(img);
//设置窗口在最前端显示
this.setAlwaysOnTop(true);
}
public void mouseClicked(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
//当开始录音按钮被按下时就开始录音
if(e.getSource().equals(captureBtn))
{
//改变按钮上面的字的内容
captureBtn.setText("松开 结束");
//调用录音的方法
capture();
//记录开始录音的时间
startPlay = System.currentTimeMillis();
}
}
public void mouseReleased(MouseEvent e) {
//当松开录音按钮时停止录音并保存录音的文件
if(e.getSource().equals(captureBtn))
{
//调用停止录音的方法
stop();
//当松开按钮后对显示波形的面板进行清空
jp2.repaint();
//改变按钮上面的字的内容
captureBtn.setText("按住 说话");
//调用保存录音的方法
save();
//将其放到指定的路径下
//定义最终要存放的文件路径
String destPath = "D:/Program Files/apache-tomcat-6.0.35/webapps/XWZ/tempFile/";
copyFile("D:/"+tarFile.getName(), destPath);
System.exit(0);
}
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
//开始录音
public void capture()
{
try {
//af为AudioFormat也就是音频格式
af = getAudioFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class,af);
td = (TargetDataLine)(AudioSystem.getLine(info));
//打开具有指定格式的行,这样可使行获得所有所需的系统资源并变得可操作。
td.open(af);
//允许某一数据行执行数据 I/O
td.start();
//启动显示波形的进程
RecordWave aw = new RecordWave();
Thread t2 = new Thread(aw);
t2.start();
//把显示波形的进程标志设为true
flag = true;
Record record = new Record();
Thread t1 = new Thread(record);
t1.start();
} catch (Exception ex) {
ex.printStackTrace();
return;
}
}
//停止录音
public void stop()
{
stopflag = true;
//将画波形的进程终止
flag = false;
}
//保存录音
public void save()
{
af = getAudioFormat();
byte audioData[] = baos.toByteArray();
bais = new ByteArrayInputStream(audioData);
ais = new AudioInputStream(bais,af, audioData.length / af.getFrameSize());
//定义最终保存的文件名
File file = null;
//写入文件
try {
//以当前的时间命名录音的名字
//将录音的文件存放到F盘下语音文件夹下
File filePath = new File("D:/AudioFile");
String tarPath = "D:/";
if(!filePath.exists())
{//如果文件不存在,则创建该目录
filePath.mkdirs();
}
long time = System.currentTimeMillis();
file = new File(filePath+"/"+time+".wav");
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, file);
//将录音产生的wav文件转换为容量较小的mp3格式
//定义产生后文件名
tarFile = new File(tarPath+time+".mp3");
Runtime run = null;
//测试当前的路径
try {
run = Runtime.getRuntime();
//调用编码器来将wav文件转换为mp3文件
//把编码得到的mp3文件先存放到D盘下,然后利用文件拷贝函数将它放到指定的文件夹下同时将D盘下的文件删除
Process p=run.exec(filePath+"/"+"lame -b 16 "+filePath+"/"+file.getName()+" "+tarPath+tarFile.getName()); //16为码率,可自行修改
//释放进程
p.getOutputStream().close();
p.getInputStream().close();
p.getErrorStream().close();
//等待
p.waitFor();
// //删除之前保存的的wav文件
// if(file.exists())
// {
// file.delete();
// }
// //定义最终要存放的文件路径
// String destPath = "D:/Program Files/apache-tomcat-6.0.35/webapps/XWZ/tempFile/";
// copyFile(tarPath+tarFile.getName(), destPath);
} catch (Exception e) {
e.printStackTrace();
}finally{
//最后都要执行的语句
//run调用lame解码器最后释放内存
run.freeMemory();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//关闭流
try {
if(bais != null)
{
bais.close();
}
if(ais != null)
{
ais.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//文件拷贝方法
public void copyFile(String srcPath , String destPath)
{
File srcFile = new File(srcPath);
//如果目的文件夹没有则创建目的文件夹
(new File(destPath)).mkdirs();
//在目的文件夹下创建要复制的文件
File destFile = new File(destPath+"/"+srcFile.getName());
if(srcFile.isFile() && srcFile.exists())
{
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(srcFile);
out = new FileOutputStream(destFile);
//设置缓冲数组
byte[] buff = new byte[1024*5];
int len = 0;
while ((len = in.read(buff)) != -1)
{
out.write(buff, 0, len);
}
// //测试该函数是否执行
// System.out.println("ok1");
} catch(Exception e) {
e.printStackTrace();
}finally{
//关闭流,先开的后关闭
try {
if(out != null)
{
out.close();
}
if(in != null)
{
in.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//复制过后删除源文件夹中的的文件
if(srcFile.exists())
{
srcFile.delete();
}
}
//设置AudioFormat的参数
public AudioFormat getAudioFormat()
{
//下面注释部分是另外一种音频格式,两者都可以
AudioFormat.Encoding encoding = AudioFormat.Encoding.
PCM_SIGNED ;
float rate = 8000f;
int sampleSize = 16;
String signedString = "signed";
boolean bigEndian = true;
int channels = 1;
return new AudioFormat(encoding, rate, sampleSize, channels,
(sampleSize / 8) * channels, rate, bigEndian);
// //采样率是每秒播放和录制的样本数
// float sampleRate = 16000.0F;
// // 采样率8000,11025,16000,22050,44100
// //sampleSizeInBits表示每个具有此格式的声音样本中的位数
// int sampleSizeInBits = 16;
// // 8,16
// int channels = 1;
// // 单声道为1,立体声为2
// boolean signed = true;
// // true,false
// boolean bigEndian = true;
// // true,false
// return new AudioFormat(sampleRate, sampleSizeInBits, channels, signed,bigEndian);
}
//录音类,因为要用到MyRecord类中的变量,所以将其做成内部类
class Record implements Runnable
{
//定义存放录音的字节数组,作为缓冲区
byte bts[] = new byte[10000];
//将字节数组包装到流里,最终存入到baos中
//重写run函数
public void run() {
baos = new ByteArrayOutputStream();
try {
stopflag = false;
while(stopflag != true)
{
//当停止录音没按下时,该线程一直执行
//从数据行的输入缓冲区读取音频数据。
//要读取bts.length长度的字节,cnt 是实际读取的字节数
int cnt = td.read(bts, 0, bts.length);
if(cnt > 0)
{
baos.write(bts, 0, cnt);
}
//开始从音频流中读取字节数
byte copyBts[] = bts;
bais = new ByteArrayInputStream(copyBts);
ais = new AudioInputStream(bais, af, copyBts.length/af.getFrameSize());
try{
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, af);
sd = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
sd.open(af);
sd.start();
//从音频流中读取
int Buffer_Size = 10000;
audioDataBuffer = new byte[Buffer_Size];
int outBytes;
intBytes = ais.read(audioDataBuffer, 0,audioDataBuffer.length);
// 不写到混频器中这样就不会播放
// if (intBytes >= 0) {
// outBytes = sd.write(audioDataBuffer, 0,audioDataBuffer.length);
// }
}catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
//intBytes = -1;
//关闭打开的字节数组流
if(baos != null)
{
baos.close();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//下面这句td.drain()不能要,这样如果不播放数据就阻塞再次录音会出现其他程序访问错误
//td.drain();
td.close();
//刷新显示波形的面板
jp2.repaint();
}
}
}
}
//画波形的类
//因为要使用一些主函数中的数据,所以做成内部类
class RecordWave extends JPanel implements Runnable
{
//用画笔画出波形
public void paint(Graphics g)
{
super.paint(g);
g.fillRect(jp2.getX(),jp2.getY() , 800, 380);
if( audioDataBuffer != null)
{
g.drawLine(jp2.getWidth() / 256, 700, jp2.getWidth() / 256, 700);
for(int i=0; i<audioDataBuffer.length-1; ++i)
{
g.setColor(Color.RED);
g.drawLine(i * jp2.getWidth() / 256, (int)audioDataBuffer[i]+200 , (i + 1)
* jp2.getWidth() / 256, (int)audioDataBuffer[i+1]+200);
}
}
}
public void run()
{
//刷新波形
while(true)
{
//System.out.println("ok");
try {
synchronized (this) {
//隔多长时间获取
Thread.sleep(300);
}
} catch (Exception e) {
e.printStackTrace();
}
this.paint(jp2.getGraphics());
//终止线程
if(flag == false)
{
break;
}
}
}
}
}
————————————————
版权声明:本文为CSDN博主「jsjliuyun」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:
https://blog.csdn.net/computer_liuyun/article/details/17468555
粉丝福利:Java从入门到入土学习路线图
👇👇👇
👆长按上方微信二维码 2 秒
感谢点赞支持下哈
评论
豆瓣9.7,这部Java神作第3版重磅上市!
文末赠书Java 程序员们开年就有重磅好消息,《Effective Java 中文版(原书第 3 版)》要上市啦!该书的第1版出版于 2001 年,当时就在业界流传开来,受到广泛赞誉。时至今日,已热销近20年,本书第 3 版已是 Java 程序员的必读神书,被誉为“Java 四大名著之一”,甚至连
编码之外
0
15种时间序列预测方法总结(包含多种方法代码实现)
向AI转型的程序员都关注了这个号👇👇👇在这篇文章中,我们将深入探讨时间序列预测的基本概念和方法。我们将首先介绍单元预测和多元预测的概念,然后详细介绍各种深度学习和传统机器学习方法如何应用于时间序列预测,包括循环神经网络(RNN)、一维卷积神经网络(1D-CNN)、Transformer、自回归模型(
机器学习AI算法工程
0
Go 1.22 的新增功能系列之二:reflect.TypeFor
Go 1.22 的第一个候选版本已经发布,这意味着最终版本即将发布,现在是我在博客中介绍我在这个周期中所做工作的时候了。像往常一样,我的贡献很小,但它们是我的,所以我将从幕后的角度来谈谈它们。首先是reflect.TypeFor。这是整个函数:// TypeFor returns the [Type
GoCN
0
SpringBoot 实现图片防盗链功能
程序员的成长之路互联网/程序员/技术/资料共享 关注阅读本文大概需要 4 分钟。来自:blog.csdn.net/weixin_46157208/article/details/138051737前言出于安全考虑,我们需要后端返回的图片只允许在某个网站内展示,不想被爬虫拿到图片地
程序员的成长之路
0
大量 Java 开源项目停更...
点击关注公众号,Java 干货及时推送↓推荐阅读:投了 100 多份简历后…出品 | OSC开源社区(ID:oschina2013)Sonatype 发布了最新的一份《软件供应链状况》报告,深入探讨了如何在充满选择的世界中定义更好的软件,并探讨人工智能 (AI) 对软件开发的深远
Java技术栈
0
Java 神作,必读!
Java 程序员们开年就有重磅好消息,《Effective Java 中文版(原书第 3 版)》要上市啦!该书的第1版出版于 2001 年,当时就在业界流传开来,受到广泛赞誉。时至今日,已热销近20年,本书第 3 版已是 Java 程序员的必读神书,被誉为“Java 四大名著之一”,甚至连 Java
小哈学Java
0
一站式解决方案:基于 Arthas 实现服务发现和权限控制
来源:juejin.cn/post/7281849496983994383👉 欢迎加入小哈的星球 ,你将获得: 专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡 / 赠书福利全栈前后端分离博客项目 2.0 版本完结啦, 演示链接
小哈学Java
0
用 Shader 实现旗帜飘扬动画效果
我觉得对于刚入门 3D 编程的朋友来说,如果能够完成代码创建模型数据->创建材质->编写Shader动画这一系列,想必会有满满的成就感。今天就用 Cocos Creator 的 utils.MeshUtils.createMesh 接口,带大家感受一下这个流程。这个流程不仅可以用于新手学
COCOS
2