本文是用Java写的,C#原理一样并已经验证
最近工作需要检测设备的是否有音频输出,找了很多资料,但是关于1Khz的验证并没有多少,所以我就自己查WAV的文件格式,并手撸了代码,来检测是否1Khz有声音,也可以检测WAV是否有声音
首先,我们需要将WAV文件读出来并将它的头文件分析,关于头文件的网上有很多文章,但是都不是很全面,头文件总会多一些或少一些无关紧要的信息,导致分析出错,下面是我读取头文件的代码(get 和 set方法自行补全)
public void readAudioFile(){
String filePath = "C:\Users\1002212\Music\baiduAudio.wav";
File file = new File(filePath);
if(!file.exists()){
System.out.println("文件不存在,请检查");
return;
}
FileInputStream fileInputStream;
try{
fileInputStream = new FileInputStream(file);
RiffFormat riffFormat = parseRiffFormat(fileInputStream);
System.out.println(riffFormat);
parse1KHZ(riffFormat);
}catch (Exception e){
e.printStackTrace();
}
}
下面是分析WAV文件的主要代码 ,因为有一些信息不一定有,所以要做一些判断,下面的代码中CDsize也是不一定有的,需要自己判断下(我比较懒,就不写了),关于分析的都是些什么,可以查看RiffFormat类
private RiffFormat parseRiffFormat(FileInputStream fileInputStream) throws Exception{
RiffFormat riffFormat = new RiffFormat();
riffFormat.setCkid(readByteBuffer(fileInputStream,4));
riffFormat.setFileSize(readInt(fileInputStream));
riffFormat.setFccType(readByteBuffer(fileInputStream,4));
byte[] buffer = readByteBuffer(fileInputStream, 4);
if(buffer[0]==0x66 && buffer[1]==0x6D && buffer[2]==0x74 && buffer[3]==0x20){
riffFormat.setWaveFormat(buffer);
} else {
riffFormat.setUnknowChar(buffer);
riffFormat.setUnknowChar2(readByteBuffer(fileInputStream, 32));
riffFormat.setWaveFormat(readByteBuffer(fileInputStream,4));
}
riffFormat.setCkSize(readInt(fileInputStream));
riffFormat.setwFormatTag(readShort(fileInputStream,true));
riffFormat.setnChannels(readShort(fileInputStream,true));
riffFormat.setnSamplesPerSec(readInt(fileInputStream));
riffFormat.setnAvgBytesPerSec(readInt(fileInputStream));
riffFormat.setnBlockAlign(readShort(fileInputStream,true));
riffFormat.setwBitsPerSample(readShort(fileInputStream,true));
riffFormat.setCbSize(readShort(fileInputStream,true));
riffFormat.setWdid(readByteBuffer(fileInputStream,4));
riffFormat.setWdSize(readInt(fileInputStream));
int wdSize = riffFormat.getWdSize();
riffFormat.setWdbuf(readByteBuffer(fileInputStream,wdSize));
return riffFormat;
}
下面是RiffFormat类,对应头文件的一些信息,分析出来的信息就对应下面的类
public class RiffFormat {
byte[] ckid; //RIFF标识
int fileSize; //文件大小
byte[] fccType; //WAVE标志
byte[] unknowChar = new byte[4];
byte[] unknowChar2 = new byte[32];
byte[] waveFormat; //WAVE格式标志
int CkSize; //块大小
short wFormatTag;//音频格式一般为WAVE_FORMAT_PCM
short nChannels;//采样声道数
int nSamplesPerSec;//采样率
int nAvgBytesPerSec;//每秒字节数 通道数*采样率*采样精度/8(字节位数)
short nBlockAlign;//块大小 采样字节*声道数
short wBitsPerSample;//采样精度 采样精度/8 = 采样字节
short cbSize; //预留字节 一般为0扩展域有的没有
byte[] wdid; //data 标志
int wdSize; //块大小
byte[] wdbuf; //数据指针 有符号
public RiffFormat(){}
public static String byte2Ascii(byte[] byteArr){
Charset cs = StandardCharsets.UTF_8;
ByteBuffer bb = ByteBuffer.allocate(byteArr.length);
bb.put(byteArr);
bb.flip();
CharBuffer cb = cs.decode(bb);
return new String(cb.array());
}
}
文件读取完了,就该来分析文件数据了,原理是先将音频文件分声道,然后读取一个声道的1秒的数据,分析其波峰是不是有1000左右(误差在15左右吧),如果不是1000再读取下一秒的数据,如果是的话就分析下一个声道
波峰计算是将声道数据拖到21长度的数组中,判断中间的数字(第10位)是否比左边和右边的数据都大,如果都大,那它是波峰无疑,如果你感觉不精确,可以将数组的长度加大,如41,但是数组的长度必须是奇数,因为好找中间数
下面就是分析音频的代码
private void parse1KHZ(RiffFormat riffFormat){
List points = new ArrayList<>();
int sizeCheck = riffFormat.getnAvgBytesPerSec();
int channel = riffFormat.getnChannels();
short bitSample = riffFormat.getwBitsPerSample();
byte[] content = new byte[sizeCheck];
System.arraycopy(riffFormat.getWdbuf(),0,content,0,sizeCheck);
short[] leftContent = new short[sizeCheck/2/2];
short[] rightContent = new short[sizeCheck/2/2];
int blockSize = riffFormat.getnBlockAlign();
int blockCount = sizeCheck/blockSize;
for(int m = 0; m < blockCount; m++){
// https://blog.csdn.net/imxiangzi/article/details/80265978
leftContent[m] = byteArrayToShort(new byte[]{content[m*4+1],content[m*4]});
rightContent[m] = byteArrayToShort(new byte[]{content[m*4+3],content[m*4+2]});
}
int cacheLength = 21;
System.out.println("rightContent length is " +rightContent.length);
short[] cache = new short[cacheLength];
for (short value : rightContent) {
short[] cacheNew = new short[cacheLength];
short bts = value;
System.arraycopy(cache, 1, cacheNew, 0, cacheLength - 1);
cacheNew[cacheLength - 1] = bts;
try {
boolean tp = isTerminalPoint(cacheNew);
if (tp) {
points.add(cacheNew[cacheLength / 2]);
}
cache = cacheNew;
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("右声道 *****"+points.size());
points.clear();
for(int i = 0; i
好了。分析就结束了,赶紧去试试吧
参考文献:https://blog.csdn.net/imxiangzi/article/details/80265978



