Java实现音频格式转换 WAV—mp3,可使音频压缩
2016-04-25 10:53
726 查看
http://www.xuebuyuan.com/1749979.html
最近做的一个小项目中,师兄安排的任务,要实现录音然后保存文件,实现网络传输,然后我用初学的java实现了一个录音机的功能(见前面的博客),但是windows录音默认保存的格式是WAVE,后缀是WAV,经过大量测试,录制一分钟要1M大小,这样不便于网络的传输,于是下面的任务就是实现音频压缩了,搜了几天,找到了一个不错的软件Monkey
Audio(实现说明,我们的项目得全是代码实现的),这个软件的开源的,但是在官网下载的源代码经过我们一个下午的测试,竟然跟最新软件的压缩差的很远,于是我们便尝试走其他的路,比如:音频格式转换也可以达到音频压缩的目的,于是便搜类似的东西,于是便找到了Lame解码器。
百度百科普及:
LAME
是最好的MP3编码器,编码高品质MP3的最好也是唯一的选择。LAME本身是DOS下的文件,需要加外壳程序才比较容易使用,也可以在别的软件(比如EAC)中间调用。是一款出色的MP3压缩程序,它使用了独创的人体听音心理学模型和声学模型,改变了人们对MP3高音发哑、低音发破的音质的印象。
然后我就通过代码在程序调用这个解码器,实现了最后的音频格式转换,当然也达到的压缩的目地,并且经过大量的测试,效果非常不错,并没有损坏音频,同时压缩比很高。
事先说明:Lame.exe文件必须得有,并且在程序调用的时候,如果没在java的src同一个文件夹下的话,必须在程序中使用绝对路径,否则报错。
贴出完整的录音代码(这次与上次博文中代码添加了音频格式转换的代码)
最近做的一个小项目中,师兄安排的任务,要实现录音然后保存文件,实现网络传输,然后我用初学的java实现了一个录音机的功能(见前面的博客),但是windows录音默认保存的格式是WAVE,后缀是WAV,经过大量测试,录制一分钟要1M大小,这样不便于网络的传输,于是下面的任务就是实现音频压缩了,搜了几天,找到了一个不错的软件Monkey
Audio(实现说明,我们的项目得全是代码实现的),这个软件的开源的,但是在官网下载的源代码经过我们一个下午的测试,竟然跟最新软件的压缩差的很远,于是我们便尝试走其他的路,比如:音频格式转换也可以达到音频压缩的目的,于是便搜类似的东西,于是便找到了Lame解码器。
百度百科普及:
LAME
是最好的MP3编码器,编码高品质MP3的最好也是唯一的选择。LAME本身是DOS下的文件,需要加外壳程序才比较容易使用,也可以在别的软件(比如EAC)中间调用。是一款出色的MP3压缩程序,它使用了独创的人体听音心理学模型和声学模型,改变了人们对MP3高音发哑、低音发破的音质的印象。
然后我就通过代码在程序调用这个解码器,实现了最后的音频格式转换,当然也达到的压缩的目地,并且经过大量的测试,效果非常不错,并没有损坏音频,同时压缩比很高。
事先说明:Lame.exe文件必须得有,并且在程序调用的时候,如果没在java的src同一个文件夹下的话,必须在程序中使用绝对路径,否则报错。
贴出完整的录音代码(这次与上次博文中代码添加了音频格式转换的代码)
/* * 实现录音机的功能 */ package com.liuyun.MyRecord1; import java.awt.*; import javax.swing.*; import java.awt.event.*; import java.io.*; import javax.sound.sampled.*; import java.lang.*; public class MyRecord extends JFrame implements ActionListener{ //定义录音格式 AudioFormat af = null; //定义目标数据行,可以从中读取音频数据,该 TargetDataLine 接口提供从目标数据行的缓冲区读取所捕获数据的方法。 TargetDataLine td = null; //定义源数据行,源数据行是可以写入数据的数据行。它充当其混频器的源。应用程序将音频字节写入源数据行,这样可处理字节缓冲并将它们传递给混频器。 SourceDataLine sd = null; //定义字节数组输入输出流 ByteArrayInputStream bais = null; ByteArrayOutputStream baos = null; //定义音频输入流 AudioInputStream ais = null; //定义停止录音的标志,来控制录音线程的运行 Boolean stopflag = false; //记录开始录音的时间 long startPlay; //定义所需要的组件 JPanel jp1,jp2,jp3; JLabel jl1=null; JButton captureBtn,stopBtn,playBtn,saveBtn; public static void main(String[] args) { //创造一个实例 MyRecord mr = new MyRecord(); } //构造函数 public MyRecord() { //组件初始化 jp1 = new JPanel(); jp2 = new JPanel(); jp3 = new JPanel(); //定义字体 Font myFont = new Font("华文新魏",Font.BOLD,30); jl1 = new JLabel("录音机功能的实现"); jl1.setFont(myFont); jp1.add(jl1); captureBtn = new JButton("开始录音"); //对开始录音按钮进行注册监听 captureBtn.addActionListener(this); captureBtn.setActionCommand("captureBtn"); //对停止录音进行注册监听 stopBtn = new JButton("停止录音"); stopBtn.addActionListener(this); stopBtn.setActionCommand("stopBtn"); //对播放录音进行注册监听 playBtn = new JButton("播放录音"); playBtn.addActionListener(this); playBtn.setActionCommand("playBtn"); //对保存录音进行注册监听 saveBtn = new JButton("保存录音"); saveBtn.addActionListener(this); saveBtn.setActionCommand("saveBtn"); this.add(jp1,BorderLayout.NORTH); this.add(jp2,BorderLayout.CENTER); this.add(jp3,BorderLayout.SOUTH); jp3.setLayout(null); jp3.setLayout(new GridLayout(1, 4,10,10)); jp3.add(captureBtn); jp3.add(stopBtn); jp3.add(playBtn); jp3.add(saveBtn); //设置按钮的属性 captureBtn.setEnabled(true); stopBtn.setEnabled(false); playBtn.setEnabled(false); saveBtn.setEnabled(false); //设置窗口的属性 this.setSize(400,300); this.setTitle("录音机"); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setLocationRelativeTo(null); this.setVisible(true); } public void actionPerformed(ActionEvent e) { if(e.getActionCommand().equals("captureBtn")) { //点击开始录音按钮后的动作 //停止按钮可以启动 captureBtn.setEnabled(false); stopBtn.setEnabled(true); playBtn.setEnabled(false); saveBtn.setEnabled(false); //调用录音的方法 capture(); //记录开始录音的时间 startPlay = System.currentTimeMillis(); }else if (e.getActionCommand().equals("stopBtn")) { //点击停止录音按钮的动作 captureBtn.setEnabled(true); stopBtn.setEnabled(false); playBtn.setEnabled(true); saveBtn.setEnabled(true); //调用停止录音的方法 stop(); //记录停止录音的时间 long stopPlay = System.currentTimeMillis(); //输出录音的时间 System.out.println("Play continues " + (stopPlay-startPlay)); }else if(e.getActionCommand().equals("playBtn")) { //调用播放录音的方法 play(); }else if(e.getActionCommand().equals("saveBtn")) { //调用保存录音的方法 save(); } } //开始录音 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(); //创建播放录音的线程 Record record = new Record(); Thread t1 = new Thread(record); t1.start(); } catch (LineUnavailableException ex) { ex.printStackTrace(); return; } } //停止录音 public void stop() { stopflag = true; } //播放录音 public void play() { //将baos中的数据转换为字节数据 byte audioData[] = baos.toByteArray(); //转换为输入流 bais = new ByteArrayInputStream(audioData); af = getAudioFormat(); ais = new AudioInputStream(bais, af, audioData.length/af.getFrameSize()); try { DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, af); sd = (SourceDataLine) AudioSystem.getLine(dataLineInfo); sd.open(af); sd.start(); //创建播放进程 Play py = new Play(); Thread t2 = new Thread(py); t2.start(); } catch (Exception e) { e.printStackTrace(); }finally{ try { //关闭流 if(ais != null) { ais.close(); } if(bais != null) { bais.close(); } if(baos != null) { baos.close(); } } catch (Exception e) { e.printStackTrace(); } } } //保存录音 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("F:/语音文件"); if(!filePath.exists()) {//如果文件不存在,则创建该目录 filePath.mkdir(); } long time = System.currentTimeMillis(); file = new File(filePath+"/"+time+".wav"); AudioSystem.write(ais, AudioFileFormat.Type.WAVE, file); //将录音产生的wav文件转换为容量较小的mp3格式 //定义产生后文件名 String tarFileName = time+".mp3"; Runtime run = null; try { run = Runtime.getRuntime(); long start=System.currentTimeMillis(); //调用解码器来将wav文件转换为mp3文件 <strong><span style="color:#ff0000;">Process p=run.exec(filePath+"/"+"lame -b 16 "+filePath+"/"+file.getName()+" "+filePath+"/"+tarFileName); //16为码率,可自行修改 </span></strong>//释放进程 p.getOutputStream().close(); p.getInputStream().close(); p.getErrorStream().close(); p.waitFor(); long end=System.currentTimeMillis(); System.out.println("convert need costs:"+(end-start)+"ms"); //删除无用的wav文件 if(file.exists()) { file.delete(); } } 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(); } } } //设置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); } } } catch (Exception e) { e.printStackTrace(); }finally{ try { //关闭打开的字节数组流 if(baos != null) { baos.close(); } } catch (IOException e) { e.printStackTrace(); }finally{ td.drain(); td.close(); } } } } //播放类,同样也做成内部类 class Play implements Runnable { //播放baos中的数据即可 public void run() { byte bts[] = new byte[10000]; try { int cnt; //读取数据到缓存数据 while ((cnt = ais.read(bts, 0, bts.length)) != -1) { if (cnt > 0) { //写入缓存数据 //将音频数据写入到混频器 sd.write(bts, 0, cnt); } } } catch (Exception e) { e.printStackTrace(); }finally{ sd.drain(); sd.close(); } } } }
相关文章推荐
- 反射机制
- JAVA--观察者模式(Observer)--设计模式十五
- JAVA基础--单例模式
- 利用Java针对MySql封装的jdbc框架类 JdbcUtils 完整实现(包含增删改查、JavaBean反射原理,附源码)
- JAVA--模版方法模式(Template Method)--设计模式十四
- Eclipse: “The import java.io cannot be resolved”
- java中关于try、catch、finally中的细节分析
- JAVA--策略模式(strategy)--设计模式十三
- Java泛型介绍!!!
- spring aop中的propagation的7种配置的意思
- JAVA--享元模式(Flyweight)--设计模式十二
- JAVA--组合模式(Composite)--设计模式十一
- import javax.servlet 出错(真的很管用)
- Exception raised during rendering: java.lang.System.arraycopy([CI[CII)V
- JAVA基础--适配器模式
- SpringMVC
- JAVA--桥接模式(Bridge)--设计模式十
- JAVA基础--代理模式
- ideaIU-15.0.1的破解和eclipse快捷键
- JAVA--外观模式(Facade)--设计模式九