2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > java实现录音并保存为wav格式的音频文件

java实现录音并保存为wav格式的音频文件

时间:2018-08-15 19:01:19

相关推荐

java实现录音并保存为wav格式的音频文件

前言:本意是想像个录屏的软件,这篇先从录音功能开始。

整体思路:采用java官方API——TargetDataLine,从声卡中采集音频数据达到录音效果,采集的数据为PCM裸流,再将PCM转为wav格式。

如果你对音频文件一点也不了解,建议看一下这篇文章中的内容,主要解析了wav文件的格式,并涉及到一些音频有关的概念:

WAV文件格式详解

然后我将分成两部分代码来讲解。

第一部分代码:从声卡中采集数据并保存为pcm文件。

package com;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;import java.util.Scanner;import javax.sound.sampled.AudioFormat;import javax.sound.sampled.AudioSystem;import javax.sound.sampled.TargetDataLine;/*** 思路:采用java官方API——TargetDataLine,从声卡中采集音频数据达到录音效果,采集的数据为PCM裸流需要转为wav格式的话参照——PCM转WAV 。* @author Administrator**/public class Sound {boolean isStop=false;//采样率private static float RATE = 44100f;//编码格式PCMprivate static AudioFormat.Encoding ENCODING = AudioFormat.Encoding.PCM_SIGNED;//帧大小 16 private static int SAMPLE_SIZE = 16;//是否大端private static boolean BIG_ENDIAN = false;//true//通道数private static int CHANNELS = 2;public void save(String path) throws Exception {//创建指定文件File file = new File(path);if(file.isDirectory()) {if(!file.exists()) {file.mkdirs();}file.createNewFile();}//设置格式AudioFormat audioFormat = new AudioFormat(ENCODING,RATE, SAMPLE_SIZE, CHANNELS, (SAMPLE_SIZE / 8) * CHANNELS,RATE, BIG_ENDIAN);//获取线路TargetDataLine targetDataLine = AudioSystem.getTargetDataLine(audioFormat);targetDataLine.open();targetDataLine.start();/**targetDataLine.read()* 从数据线的输入缓冲区读取音频数据,该方法会阻塞,当数据先关闭之后就不会阻塞了*/Thread thread=new Thread() {int flag = 0;OutputStream os = new FileOutputStream(file);byte[] b = new byte[256];public void run() {while((flag = targetDataLine.read(b, 0, b.length))>0) {//从声卡中采集数据try {os.write(b);} catch (IOException e) {// TODO 自动生成的 catch 块e.printStackTrace();}//System.out.println(flag);if(isStop) {isStop=false;break;}}}};thread.start();//监听按键Thread thread2=new Thread() {public void run() {Scanner in=new Scanner(System.in);if(in.next().equals("s")) {isStop=true;}}};thread2.start();}}

第二部分代码:将pcm文件转换为wav格式的文件。

package com;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class PcmToWave {/*** * @param src* src[0]指定pcm文件位置,src[1]指定输出的wav文件存放位置* @throws Exception*/public static void convertAudioFiles(String[] src) throws Exception {FileInputStream fis = new FileInputStream(src[0]);//获取PCM文件大小File file=new File(src[0]);int PCMSize =(int) file.length();//定义wav文件头//填入参数,比特率等等。这里用的是16位单声道 8000 hzWaveHeader header = new WaveHeader(PCMSize);//长度字段 = 内容的大小(PCMSize) + 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)header.fileLength = PCMSize + (44 - 8);header.FmtHdrLeth = 16;header.BitsPerSample = 16;header.Channels = 1;header.FormatTag = 0x0001;header.SamplesPerSec = 44100;//8000;header.BlockAlign = (short)(header.Channels * header.BitsPerSample / 8);header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;header.DataHdrLeth = PCMSize;//获取wav文件头字节数组byte[] h = header.getHeader();assert h.length == 44; //WAV标准,头部应该是44字节System.out.println((PCMSize+44));// auline.write(h, 0, h.length);byte[] b = new byte[10];//将文件头写入文件FileOutputStream fs = new FileOutputStream(src[1]);fs.write(h);//将pcm文件写到文件头后面FileInputStream fiss = new FileInputStream(src[0]);byte[] bb = new byte[10];int len = -1;while((len = fiss.read(bb))>0) {fs.write(bb, 0, len);}}}/*** WavHeader辅助类。用于生成头部信息。* @author Administrator**/class WaveHeader {/**wav文件头:RIFF区块*名称偏移地址字节数端序内容* ID0x004Byte大端'RIFF' (0x52494646)Size0x044Byte小端fileSize - 8Type0x084Byte大端'WAVE'(0x57415645)解析:以'RIFF'为标识Size是整个文件的长度减去ID和Size的长度Type是WAVE表示后面需要两个子块:Format区块和Data区块*//*** FORMAT区块:* 名称偏移地址字节数端序内容ID0x004Byte大端'fmt ' (0x666D7420)Size0x044Byte小端16AudioFormat0x082Byte小端音频格式NumChannels0x0A2Byte小端声道数SampleRate0x0C4Byte小端采样率ByteRate0x104Byte小端每秒数据字节数BlockAlign0x142Byte小端数据块对齐BitsPerSample0x162Byte小端采样位数解析:以'fmt '为标识Size表示该区块数据的长度(不包含ID和Size的长度)AudioFormat表示Data区块存储的音频数据的格式,PCM音频数据的值为1NumChannels表示音频数据的声道数,1:单声道,2:双声道SampleRate表示音频数据的采样率ByteRate每秒数据字节数 = SampleRate * NumChannels * BitsPerSample / 8BlockAlign每个采样所需的字节数 = NumChannels * BitsPerSample / 8BitsPerSample每个采样存储的bit数,8:8bit,16:16bit,32:32bit*//*** DATA区块* * 名称偏移地址字节数端序内容ID0x004Byte大端'data' (0x64617461)Size0x044Byte小端NData0x08NByte小端音频数据解析:以'data'为标识Size表示音频数据的长度,N = ByteRate * secondsData音频数据*/public final char fileID[] = {'R', 'I', 'F', 'F'};public int fileLength;public short FormatTag;public short Channels;public int SamplesPerSec;public int AvgBytesPerSec;public short BlockAlign;public short BitsPerSample;public char DataHdrID[] = {'d','a','t','a'};public int DataHdrLeth;public char wavTag[] = {'W', 'A', 'V', 'E'};;public char FmtHdrID[] = {'f', 'm', 't', ' '};public int FmtHdrLeth;public WaveHeader() {}//无参构造方法/*** * @param a*/public WaveHeader(int a) {}public byte[] getHeader() throws IOException {//创建一个输出流,用于将各个字节数组写入缓存中,缓存区会自动增长。然后可以将整个输出流转换为完整的字节数组,关闭该流不会有任何效果。ByteArrayOutputStream bos = new ByteArrayOutputStream();WriteChar(bos, fileID);WriteInt(bos, fileLength);WriteChar(bos, wavTag);WriteChar(bos, FmtHdrID);WriteInt(bos,FmtHdrLeth);WriteShort(bos,FormatTag);WriteShort(bos,Channels);WriteInt(bos,SamplesPerSec);WriteInt(bos,AvgBytesPerSec);WriteShort(bos,BlockAlign);WriteShort(bos,BitsPerSample);WriteChar(bos,DataHdrID);WriteInt(bos,DataHdrLeth);bos.flush();byte[] r = bos.toByteArray();bos.close();return r;}private void WriteShort(ByteArrayOutputStream bos, int s) throws IOException {byte[] mybyte = new byte[2];mybyte[1] =(byte)( (s << 16) >> 24 );//存放高位mybyte[0] =(byte)( (s << 24) >> 24 );//存放低位bos.write(mybyte);}private void WriteInt(ByteArrayOutputStream bos, int n) throws IOException {byte[] buf = new byte[4];buf[3] =(byte)( n >> 24 );buf[2] =(byte)( (n << 8) >> 24 );buf[1] =(byte)( (n << 16) >> 24 );buf[0] =(byte)( (n << 24) >> 24 );bos.write(buf);}private void WriteChar(ByteArrayOutputStream bos, char[] id) {for (int i=0; i<id.length; i++) {char c = id[i];bos.write(c);}}}

这个需要注意的一件事就是,wav格式的文件是以小端形式来存储的,即低位存放在低位内存中,高位存放在高位内存中。所以你在生成pcm文件的时候,一定要以小端形式存储数据,否则播放音频文件的时候完全是混乱的杂音。另外,还需要注意的是,pcm文件与wav文件的采样率要一致,它会根据采样率来计算播放时长。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。