PCM轉(zhuǎn)WAV只需在PCM文件前面增加WAV頭即可,WAV文件頭中包含的信息有:采樣率、采樣的位數(shù)、文件大小等。頭文件的長度占44字節(jié)。
采樣的位數(shù)指的是描述數(shù)字信號(hào)所使用的位數(shù)。8位(8bit)代表2的8次方=256,16 位(16bit)則代表2的16次方=65536 / 1024 =64K
采樣率是一秒鐘內(nèi)對(duì)聲音信號(hào)的采樣次數(shù)
網(wǎng)絡(luò)接收一個(gè)音頻的時(shí)長是20ms, 已知音頻采樣率是8kHz,采樣的位數(shù)是16bit。
[時(shí)長]20ms * [采樣率]8kHz * [采樣的位數(shù)]16bit = 320 byte
例如,CD碟采用16位的采樣精度,44.1KHz的采樣頻率,為雙聲道,它每秒所需要的數(shù)據(jù)量為16×44100×2÷8=176400字節(jié)。這樣算下來,比特率應(yīng)該是1400多Kbps,如果采用MP3、WMA編碼格式,比特率能夠更小。
WAV文件的基本格式 |
類型 | 內(nèi)容 | 變量名 | 大小 | 取值 |
RIFF頭 |
文件標(biāo)識(shí)符串 |
fileId |
4B |
"RIFF" |
頭后文件長度 |
fileLen |
4B |
非負(fù)整數(shù)(=文件長度-8) |
數(shù)據(jù)類型標(biāo)識(shí)符 |
波形文件標(biāo)識(shí)符 |
waveId |
4B |
"WAVE" |
格式塊 |
塊頭 |
格式塊標(biāo)識(shí)符 |
chkId |
4B |
"fmt" |
頭后塊長度 |
chkLen |
4B |
非負(fù)整數(shù)(=16或18) |
塊數(shù)據(jù) |
格式標(biāo)記 |
wFormatTag |
2B |
非負(fù)短整數(shù)(PCM=1) |
聲道數(shù) |
wChannels |
2B |
非負(fù)短整數(shù)(=1或2) |
采樣率 |
dwSampleRate |
4B |
非負(fù)整數(shù)(單聲道采樣數(shù)/秒) |
平均字節(jié)率 |
dwAvgBytesRate |
4B |
非負(fù)整數(shù)(字節(jié)數(shù)/秒) |
數(shù)據(jù)塊對(duì)齊 |
wBlockAlign |
2B |
非負(fù)短整數(shù)(不足補(bǔ)零) |
采樣位數(shù) |
wBitsPerSample |
2B |
非負(fù)短整數(shù)(PCM時(shí)才有) |
擴(kuò)展域大小 |
wExtSize |
2B |
非負(fù)短整數(shù) |
可選(根據(jù)chkLen=16或18判斷) |
擴(kuò)展域 |
extraInfo |
extSize B |
擴(kuò)展信息 |
數(shù)據(jù)塊 |
塊頭 |
數(shù)據(jù)塊標(biāo)識(shí)符串 |
chkId |
4B |
"data" |
頭后塊長度 |
chkLen |
4B |
非負(fù)整數(shù) |
塊數(shù)據(jù) |
波形采樣數(shù)據(jù) |
x 或 xl、xr |
chkLen B |
左右聲道樣本交叉排列
樣本值為整數(shù)(整字節(jié)存儲(chǔ),不足位補(bǔ)零),整個(gè)數(shù)據(jù)塊按blockAlign對(duì)齊 |
示例:
package com.pvcp.common.utils;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class PcmToWavUtil {
public static boolean convert(String pcmFilePath, String wavFilePath, int bitsPerSample, int samplesPerSec) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(pcmFilePath);
fos = new FileOutputStream(wavFilePath);
// 計(jì)算長度
int pcmSize = fis.available();// 由于音頻文件一般不會(huì)大于 Int 最大值,所以使用 available
// 填入?yún)?shù),比特率等等。這里用的是 16位 單聲道 8000 hz
WavHeader header = new WavHeader();
// 長度字段 = 內(nèi)容的大小(PCMSize) + 頭部字段的大小(不包括前面4字節(jié)的標(biāo)識(shí)符RIFF以及fileLength本身的4字節(jié))
header.fileLength = pcmSize + (44 - 8);
header.fmtHdrLeth = 16;
header.bitsPerSample = (short)bitsPerSample;
header.channels = 1;
header.formatTag = 0x0001;
// header.samplesPerSec = 8000;
header.samplesPerSec = samplesPerSec;
header.blockAlign = (short) (header.channels * header.bitsPerSample / 8);
header.avgBytesPerSec = header.blockAlign * header.samplesPerSec;
header.dataHdrLeth = pcmSize;
byte[] h = header.getHeader();
assert h.length == 44;// WAV標(biāo)準(zhǔn),頭部應(yīng)該是44字節(jié)
// write header
fos.write(h, 0, h.length);
// write data stream
SourceUtils.write(fis, fos);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
SourceUtils.close(fis, fos);
}
}
}
package com.pvcp.common.utils;
import java.io.Closeable;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class SourceUtils {
private static Log logging = LogFactory.getLog(SourceUtils.
class);
public static void runMethod(String methodName,Object

objs){
if(objs ==
null){
return;
}
for(Object obj:objs){
if(obj ==
null){
return;
}
try {
Method m = obj.getClass().getDeclaredMethod(methodName);
m.invoke(obj);
}
catch (Exception e) {
logging.error("執(zhí)行["+methodName+"]方法異常!對(duì)象:"+obj+";異常:"+e.getMessage());
}
}
}
public static void write(InputStream in,OutputStream out){
try {
byte[] buff =
new byte[1024*5];
int len = 0;
while((len=in.read(buff)) != -1){
try {
out.write(buff,0,len);
out.flush();
}
catch (Exception e) {
}
}
}
catch (Exception e) {
}
}
public static void close(Object

objs){
if(objs ==
null){
return;
}
for(Object obj:objs){
if(obj ==
null){
return;
}
try {
if(obj
instanceof Closeable){
((Closeable) obj).close();
}
else if(obj
instanceof AutoCloseable){
((AutoCloseable) obj).close();
}
else{
runMethod("close", obj);
}
}
catch (Exception e) {
logging.error("關(guān)閉資源異常!對(duì)象:"+obj+";異常:"+e.getMessage(), e);
}
}
}
}
package com.pvcp.common.utils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class WavHeader {
/**
* 4 資源交換文件標(biāo)志(RIFF)
*/
public final char fileID[] = { 'R', 'I', 'F', 'F' };
/**
* 4 總字節(jié)數(shù)
*/
public int fileLength;
/**
* 4 WAV文件標(biāo)志(WAVE)
*/
public char wavTag[] = { 'W', 'A', 'V', 'E' };
/**
* 4 波形格式標(biāo)志(fmt ),最后一位空格
*/
public char fmtHdrID[] = { 'f', 'm', 't', ' ' };
/**
* 4 過濾字節(jié)(一般為00000010H),若為00000012H則說明數(shù)據(jù)頭攜帶附加信息
*/
public int fmtHdrLeth;
/**
* 2 格式種類(值為1時(shí),表示數(shù)據(jù)為線性PCM編碼)
*/
public short formatTag;
/**
* 2 通道數(shù),單聲道為1,雙聲道為2
*/
public short channels;
/**
* 4 采樣頻率
*/
public int samplesPerSec;
/**
* 4 波形數(shù)據(jù)傳輸速率(每秒平均字節(jié)數(shù))
*/
public int avgBytesPerSec;
/**
* 2 DATA數(shù)據(jù)塊長度,字節(jié)
*/
public short blockAlign;
/**
* 2 PCM位寬
*/
public short bitsPerSample;
/**
* 4 數(shù)據(jù)標(biāo)志符(data)
*/
public char dataHdrID[] = { 'd', 'a', 't', 'a' };
/**
* 4 DATA總數(shù)據(jù)長度字節(jié)
*/
public int dataHdrLeth;
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);
}
}
}