前言:本意是想像个录屏的软件,这篇先从录音功能开始。
整体思路:采用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区块* 名称      偏移地址    字节数 端序  内容*     ID      0x00    4Byte   大端  'RIFF' (0x52494646)Size   0x04    4Byte   小端  fileSize - 8Type    0x08    4Byte   大端  'WAVE'(0x57415645)解析:以'RIFF'为标识Size是整个文件的长度减去ID和Size的长度Type是WAVE表示后面需要两个子块:Format区块和Data区块*//*** FORMAT区块:*    名称              偏移地址    字节数 端序  内容ID                0x00    4Byte   大端  'fmt ' (0x666D7420)Size           0x04    4Byte   小端  16AudioFormat       0x08    2Byte   小端  音频格式NumChannels     0x0A    2Byte   小端  声道数SampleRate       0x0C    4Byte   小端  采样率ByteRate     0x10    4Byte   小端  每秒数据字节数BlockAlign       0x14    2Byte   小端  数据块对齐BitsPerSample  0x16    2Byte   小端  采样位数解析:以'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区块* * 名称      偏移地址    字节数 端序  内容ID        0x00    4Byte   大端  'data' (0x64617461)Size   0x04    4Byte   小端  NData   0x08    NByte   小端  音频数据解析:以'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文件的采样率要一致,它会根据采样率来计算播放时长。

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

  1. html中怎么写播放音乐格式,HTML+CSS入门 如何实现多浏览器播放wav格式的音频文件...

    本篇教程介绍了HTML+CSS入门 如何实现多浏览器播放wav格式的音频文件,希望阅读本篇文章以后大家有所收获,帮助大家HTML+CSS入门. < 使用audioplayer.js 基本上能支持 ...

  2. 用C#来播放.wav格式的音频文件

    .net自带的有播放.wav格式音频文件的类:System.Media.SoundPlayer,只要调用这个类就可以了. 代码如下:     string path = "....../so ...

  3. html手机录音为wav文件,【未解决】用WebAudioRecorder.js去实现录音并保存为wav格式...

    折腾: 期间,看到这个库: 但是好像获取不到麦克风: 继续想办法看看: 还是:Could not get audio media device: TypeError: Failed to execut ...

  4. wav格式的音频文件 16位转化成8位的

    1.下载 安装cool edit文件 http://www.xue51.com/soft/1252.html 2.file->batch file convert 2.添加文件 3.选择要修改的 ...

  5. Android 录音机小米商业项目开源代码 AudioRecord录音暂停 播放 Wav格式(音频二)

    Android MediaRecorder录音录像 暂停 继续录音 播放 ARM格式(音频一) https://blog.csdn.net/WHB20081815/article/details/88 ...

  6. 录制wav格式的音频

    项目中有面部认证.声纹认证,服务器端要求上传wav格式的音频,所以写了这样一个小demo. 刚刚开始写博客还不知道怎么上传代码,就复制了,嘻嘻 DotimeManage.h @class Dotime ...

  7. 用Python编写录屏程序将播放的视频用截屏方法转换为多帧图像编辑后保存为GIF格式动图文件

    有时编写游戏后发博文,为使读者有一个直观的游戏效果,会把游戏运行动画转换GIF格式动图发到博文中.本文介绍如何用python PIL库ImageGrab.grab()函数截屏,编写录屏程序,将视频转换 ...

  8. vuejs项目前端纯js在线下载网页内容保存为自定义格式的word文件、另存为word文件

    所有前端导入导出方法集合: 前端必备技能知识:JS导出Blob流文件为Excel表格.Vue.js使用Blob的方式实现excel表格的下载(流文件下载)_勤动手多动脑少说多做厚积薄发-CSDN博客_ ...

  9. PDMReader软件,打开.pdm文件,提示要将这个pdm文件重新保存为xml格式的pdm文件,但是保存的明明就是xml格式的pdm文件,但还是不行,为什么?

    PDMReader软件,打开.pdm文件,提示要将这个pdm文件重新保存为xml格式的pdm文件,但是保存的明明就是xml格式的pdm文件,但还是不行,为什么? pdmReader的版本和powerd ...

最新文章

  1. 计算机科学 生物技术,计算机科学(Computer Science)简介
  2. C++: 构造函数和析构函数
  3. Spring Boot笔记-404错误统一管理
  4. np.unique( )--去除数组中的重复数字,并进行排序之后输出
  5. Go语言获取文件的文件路径、文件名、扩展名
  6. 联想服务器自动关机_IBM 联想 DELL HP服务器自动关机|解决办法整理
  7. 2020年java最新招聘_2021年北京Java 工程师招聘-北京Java 工程师招聘求职信息-拉勾招聘...
  8. size ar objdump readelf binutils
  9. python爬取qq好友网络状态_Python爬虫实战----爬取QQ空间好友说说并生成词云(超详细)...
  10. C++ std::shared_ptr是什么意思
  11. Windows编程入门
  12. QT 简单应用之播放WAV声音
  13. Django中URL和View的关系
  14. 计算机组装与维护doc,《计算机组装与维护》课程教案.doc
  15. 111-unsplash-for-chrome-2021-07-22
  16. 许晓斌_Maven实战(六)——Gradle,构建工具的未来?
  17. 苹果IOS9为加强个人隐私信息保护强制HTTPS,APP开发者需申请SSL证书
  18. C++ read函数与write函数
  19. 如何区分PNP型和NPN型?
  20. 【MyBatis】 MyBatis与MyBatis-Plus的区别

热门文章

  1. .NET图表控件TeeChart for .NET使用教程:图例设计
  2. Python学习系列 -- 改善 Python 程序的 91 个建议
  3. 通过有限差分和matlab矩阵运算直接求解一维薛定谔方程,通过有限差分和MATLAB矩阵运算直接求解一维薛定谔方程.doc...
  4. c语言程序从main()函数开始执行,所以这个函数要写在( ).,C语言程序从main()函数开始执行,所以这个函数要写在( )。...
  5. Markdown 链接的写法
  6. ComponentOne 控件界的“瑞士军刀”
  7. 学习51单片机外部总线扩展并口芯片8255
  8. Android 中Theme主题使用
  9. Java Web_JavaScript
  10. linux命令取消别名的几种方式