关于ADPCM编码和PCM编码的.wav文件通过Java进行相互转换
最近在做一个呼叫器的项目,中间碰到了点问题,就是呼叫器那边传过来的数据是ADPCM编码格式的,经过程序转换成.wav文件后可以在播放器上进行播放,但是无法在浏览器上进新播放。在网上查资料的时候,几乎都是C语言的版本,在程序中无法使用,就参考C语言用Java写了个编解码程序。
对.wav文件做编码转换,就需要对wav文件的格式进行了解,网上有很多wav文件格式解析的文章。我总结了一下,做了一个表格。
代码里面有些重复的,没有进行优化缩减,用到的可以自行优化一下
wav头中的RIFF块
/**
* @Description TODO
* @Author liminghui
* @Date 2020/3/14 9:14
* @Version 1.0
**/
public class RIFF {
private String RIFFID;
private Long RIFFSize;
private String RIFFType;
public RIFF() {
this.RIFFID = "RIFF";
this.RIFFType = "WAVE";
}
public String getRIFFID() {
return RIFFID;
}
public void setRIFFID(String RIFFID) {
this.RIFFID = RIFFID;
}
public Long getRIFFSize() {
return RIFFSize;
}
public void setRIFFSize(Long RIFFSize) {
this.RIFFSize = RIFFSize;
}
public String getRIFFType() {
return RIFFType;
}
public void setRIFFType(String RIFFType) {
this.RIFFType = RIFFType;
}
}
wav头中的fmt 块
/**
* @Description TODO
* @Author liminghui
* @Date 2020/3/14 9:14
* @Version 1.0
**/
public class FORMAT {
private String FORMATID;
private Long FORMATSize;
private Integer FORMATAudioFormat;
private Integer FORMATNumChannels;
private Long FORMATSampleRate;
private Long FORMATByteRate;
private Integer FORMATBlockAlign;
private Integer FORMATBitsPerSample;
private Integer FORMATsbSize;
private Integer FORMATSamplesPerBlock;
public FORMAT(String str) {
if (str == "ADPCM") {
this.FORMATID = "fmt ";
this.FORMATSize = 20l;
this.FORMATAudioFormat = 17;
this.FORMATNumChannels = 1;
this.FORMATSampleRate = 8000l;
this.FORMATByteRate = 4055l;
this.FORMATBlockAlign = 256;
this.FORMATBitsPerSample = 4;
this.FORMATsbSize = 2;
this.FORMATSamplesPerBlock = 505;
} else {
this.FORMATID = "fmt ";
this.FORMATSize = 16l;
this.FORMATAudioFormat = 1;
this.FORMATNumChannels = 1;
this.FORMATSampleRate = 8000l;
this.FORMATByteRate = 16000l;
this.FORMATBlockAlign = 2;
this.FORMATBitsPerSample = 16;
}
}
public String getFORMATID() {
return FORMATID;
}
public void setFORMATID(String FORMATID) {
this.FORMATID = FORMATID;
}
public Long getFORMATSize() {
return FORMATSize;
}
public void setFORMATSize(Long FORMATSize) {
this.FORMATSize = FORMATSize;
}
public Integer getFORMATAudioFormat() {
return FORMATAudioFormat;
}
public void setFORMATAudioFormat(Integer FORMATAudioFormat) {
this.FORMATAudioFormat = FORMATAudioFormat;
}
public Integer getFORMATNumChannels() {
return FORMATNumChannels;
}
public void setFORMATNumChannels(Integer FORMATNumChannels) {
this.FORMATNumChannels = FORMATNumChannels;
}
public Long getFORMATSampleRate() {
return FORMATSampleRate;
}
public void setFORMATSampleRate(Long FORMATSampleRate) {
this.FORMATSampleRate = FORMATSampleRate;
}
public Long getFORMATByteRate() {
return FORMATByteRate;
}
public void setFORMATByteRate(Long FORMATByteRate) {
this.FORMATByteRate = FORMATByteRate;
}
public Integer getFORMATBlockAlign() {
return FORMATBlockAlign;
}
public void setFORMATBlockAlign(Integer FORMATBlockAlign) {
this.FORMATBlockAlign = FORMATBlockAlign;
}
public Integer getFORMATBitsPerSample() {
return FORMATBitsPerSample;
}
public void setFORMATBitsPerSample(Integer FORMATBitsPerSample) {
this.FORMATBitsPerSample = FORMATBitsPerSample;
}
public Integer getFORMATsbSize() {
return FORMATsbSize;
}
public void setFORMATsbSize(Integer FORMATsbSize) {
this.FORMATsbSize = FORMATsbSize;
}
public Integer getFORMATSamplesPerBlock() {
return FORMATSamplesPerBlock;
}
public void setFORMATSamplesPerBlock(Integer FORMATSamplesPerBlock) {
this.FORMATSamplesPerBlock = FORMATSamplesPerBlock;
}
wav头中的FACT 块
/**
* @Description TODO
* @Author liminghui
* @Date 2020/3/14 9:14
* @Version 1.0
**/
public class FACT {
private String FACTID;
private Long FACTSize;
private Long FACTSampleLength;
public FACT() {
this.FACTID = "fact";
this.FACTSize = 4l;
}
public Long getFACTSampleLength() {
return FACTSampleLength;
}
public void setFACTSampleLength(Long FACTSampleLength) {
this.FACTSampleLength = FACTSampleLength;
}
public Long getFACTSize() {
return FACTSize;
}
public void setFACTSize(Long FACTSize) {
this.FACTSize = FACTSize;
}
public String getFACTID() {
return FACTID;
}
public void setFACTID(String FACTID) {
this.FACTID = FACTID;
}
}
wav头中的DATA 块
/**
* @Description TODO
* @Author liminghui
* @Date 2020/3/14 9:14
* @Version 1.0
**/
public class DATA {
private String DATAID;
private Long DATASize;
public DATA() {
this.DATAID = "data";
}
public Long getDATASize() {
return DATASize;
}
public void setDATASize(Long DATASize) {
this.DATASize = DATASize;
}
public String getDATAID() {
return DATAID;
}
public void setDATAID(String DATAID) {
this.DATAID = DATAID;
}
}
wav头中的BLOCKHEARD 块
/**
* @Description TODO
* @Author liminghui
* @Date 2020/3/14 9:16
* @Version 1.0
**/
public class BLOCKHEARD {
private Integer BLOCKPresample;
private Integer BLOCKIndex;
private Integer BLOCKRSV;
public BLOCKHEARD() {
this.BLOCKRSV = 0;
}
public Integer getBLOCKPresample() {
return BLOCKPresample;
}
public void setBLOCKPresample(Integer BLOCKPresample) {
this.BLOCKPresample = BLOCKPresample;
}
public Integer getBLOCKIndex() {
return BLOCKIndex;
}
public void setBLOCKIndex(Integer BLOCKIndex) {
this.BLOCKIndex = BLOCKIndex;
}
public Integer getBLOCKRSV() {
return BLOCKRSV;
}
public void setBLOCKRSV(Integer BLOCKRSV) {
this.BLOCKRSV = BLOCKRSV;
}
}
/**
* WAV文件以小端形式来进行数据存储。
* 所谓的大端模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
* 所谓的小端模式,是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。
* 以四个字节存储一个数据为例:类似于字符串,从右到左是从低位到高位
*/
编码encoder
package enCoder;
import entity.*;
import java.io.*;
/**
* @Description TODO
* @Author liminghui
* @Date 2020/3/13 15:13
* @Version 1.0
**/
public class encoder02 {
private static int[] indexTable={
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8};
private static int[] stepsizeTable = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767};
private static FileOutputStream fos = null;
private static FileInputStream fis = null;
private static RIFF riff = null;
private static FORMAT format = null;
private static DATA data = null;
private static FACT fact = null;
private static BLOCKHEARD blockheard = null;
public static void main(String[] args) {
try {
File inFile = new File("C:\\Users\\Administrator\\Desktop\\pcm文档\\9110999302110.wav");
File outFile = new File("C:\\Users\\Administrator\\Desktop\\pcm文档\\ddd.wav");
if (!outFile.exists()) outFile.createNewFile();
fis = new FileInputStream(inFile);
fos = new FileOutputStream(outFile);
long length = inFile.length();
writeHeard(length);
blockheard=new BLOCKHEARD();
//设置默认的index和rsv
blockheard.setBLOCKIndex(0);
blockheard.setBLOCKRSV(0);
byte[] inData=new byte[1024];
//读取前44个字节的PCM编码的WAV头
fis.read(inData, 0, 44);
int len=0;
while (len != -1) {
byte[] bytes = new byte[2];
fis.read(bytes,0,2);
blockheard.setBLOCKPresample((bytes[0] & 0xff) | bytes[1] << 8);
fos.write(Hex2ByteDTX(blockheard.getBLOCKPresample()));
fos.write(HexByteDTX(blockheard.getBLOCKIndex()));
fos.write(HexByteDTX(blockheard.getBLOCKRSV()));
len = fis.read(inData, 0, 1008);
if (len != -1) {
CoderRealize(inData, len, blockheard);
}
}
fis.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void CoderRealize(byte[] inData, int len, BLOCKHEARD blockheard) {
try {
int sign;
int delta;
int step;
int valpred;
int vpdiff;
int diff;
int index;
boolean bufferstep;
int outputbuffer = 0;
valpred = blockheard.getBLOCKPresample();
index = blockheard.getBLOCKIndex();
step = stepsizeTable[index];
bufferstep = true;
for (int i = 0; i < len / 2; i++) {
int val = (inData[i * 2] & 0xff) | inData[i * 2 + 1] << 8 ;
diff = val - valpred;
sign = (diff < 0) ? 8 : 0;
if (sign != 0) {
diff = (-diff);
}
delta = 0;
vpdiff = (step >> 3);
if (diff >= step) {
delta = 4;
diff -= step;
vpdiff += step;
}
step >>= 1;
if (diff >= step) {
delta |= 2;
diff -= step;
vpdiff += step;
}
step >>= 1;
if (diff >= step) {
delta |= 1;
vpdiff += step;
}
if (sign != 0)
valpred -= vpdiff;
else
valpred += vpdiff;
if (valpred > 32767)
valpred = 32767;
else if (valpred < -32768)
valpred = -32768;
delta |= sign;
index += indexTable[delta];
if (index < 0) index = 0;
if (index > 88) index = 88;
step = stepsizeTable[index];
if (bufferstep) {
outputbuffer = delta & 0x0f;
} else {
fos.write((byte) ((delta << 4) & 0xf0) | outputbuffer);
}
bufferstep = !bufferstep;
}
if (!bufferstep) {
fos.write(outputbuffer & 0xff);
}
blockheard.setBLOCKIndex(index);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 16进制大端转小端
* WAV文件以小端形式来进行数据存储。
*/
private static byte[] Hex4ByteDTX(Long l) {
int len = 4;
byte[] b = new byte[len];
for (int i = 0; i < len; i++) {
b[i] = (byte) (l >> (i * 8) & 0x00ff);
}
return b;
}
private static byte[] Hex2ByteDTX(int l) {
int len = 2;
byte[] b = new byte[len];
for (int i = 0; i < len; i++) {
b[i] = (byte) (l >> (i * 8) & 0x00ff);
}
return b;
}
private static byte HexByteDTX(int l) {
byte b = (byte) (l & 0x00ff);
return b;
}
private static void writeHeard(long length) {
long dataSize = (length - 44) / 1010 * 256;
if ((length - 44) % 1010 != 0) {
dataSize += ((length - 44) % 1010 - 2) / 4 + 4;
}
riff = new RIFF();
format = new FORMAT("ADPCM");
data = new DATA();
fact = new FACT();
fact.setFACTSampleLength(dataSize);
data.setDATASize(dataSize);
riff.setRIFFSize(dataSize + 58);
try {
//写入RIFF块
fos.write(riff.getRIFFID().getBytes());
fos.write(Hex4ByteDTX(riff.getRIFFSize()));
fos.write(riff.getRIFFType().getBytes());
//写入‘fmt ’块
fos.write(format.getFORMATID().getBytes());
fos.write(Hex4ByteDTX(format.getFORMATSize()));
fos.write(Hex2ByteDTX(format.getFORMATAudioFormat()));
fos.write(Hex2ByteDTX(format.getFORMATNumChannels()));
fos.write(Hex4ByteDTX(format.getFORMATSampleRate()));
fos.write(Hex4ByteDTX(format.getFORMATByteRate()));
fos.write(Hex2ByteDTX(format.getFORMATBlockAlign()));
fos.write(Hex2ByteDTX(format.getFORMATBitsPerSample()));
fos.write(Hex2ByteDTX(format.getFORMATsbSize()));
fos.write(Hex2ByteDTX(format.getFORMATSamplesPerBlock()));
//写入FACT块
fos.write(fact.getFACTID().getBytes());
fos.write(Hex4ByteDTX(fact.getFACTSize()));
fos.write(Hex4ByteDTX(fact.getFACTSampleLength()));
//写入Data块
fos.write(data.getDATAID().getBytes());
fos.write(Hex4ByteDTX(data.getDATASize()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
解码decoder
package deCoder;
import entity.BLOCKHEARD;
import entity.DATA;
import entity.FORMAT;
import entity.RIFF;
import java.io.*;
/**
* @Description TODO
* @Author liminghui
* @Date 2020/3/13 15:13
* @Version 1.0
**/
public class decoder {
private static int[] indexTable={
-1, -1, -1, -1, 2, 4, 6, 8,
-1, -1, -1, -1, 2, 4, 6, 8};
private static int[] stepsizeTable = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767};
private static FileOutputStream fos = null;
private static FileInputStream fis = null;
private static RIFF riff = null;
private static FORMAT format = null;
private static DATA data = null;
private static BLOCKHEARD blockheard = null;
public static void main(String[] args) {
try {
File inFile = new File("C:\\Users\\Administrator\\Documents\\WeChat Files\\LMH13073784256\\FileStorage\\File\\2020-05\\888.pcm");
File outFile = new File("C:\\Users\\Administrator\\Desktop\\pcm文档\\9110999302110.wav");
if (!outFile.exists()) outFile.createNewFile();
fis = new FileInputStream(inFile);
fos = new FileOutputStream(outFile);
long length = inFile.length();
writeHeard(length);
blockheard=new BLOCKHEARD();
byte[] inData=new byte[1024];
int len;
while ((len = fis.read(inData, 0, 256)) != -1) {
blockheard.setBLOCKPresample(inData[0]|inData[1]<<8);
blockheard.setBLOCKIndex((int) inData[2]);
blockheard.setBLOCKRSV((int) inData[3]);
fos.write(inData[0]);
fos.write(inData[1]);
CoderRealize(inData, len, blockheard);
}
fis.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void CoderRealize(byte[] inData, int len, BLOCKHEARD blockheard) {
int sign;
int delta;
int step;
int valpred;
int vpdiff;
int index;
boolean bufferstep;
int j=4;
valpred = blockheard.getBLOCKPresample();
index = blockheard.getBLOCKIndex();
step = stepsizeTable[index];
bufferstep = true;
for (int i = 0; i < (len - 4) * 2; i++) {
try {
if (bufferstep) {
//取低四位
delta = inData[j] & 0xf;
} else {
//取高四位
delta = (inData[j] >> 4) & 0xf;
j++;
}
bufferstep = !bufferstep;
index += indexTable[delta];
if (index < 0) index = 0;
if (index > 88) index = 88;
//取符号位 &(与运算)都为1才为1
sign = delta & 8;//sing只会等于8或者0
//取数据
delta = delta & 7;
//下边四则运算将vpdiff = (delta+0.5)*step/4四则运算转换成了二进制的与或运算(牛逼)
vpdiff = step >> 3;
if ((delta & 4) != 0) {
vpdiff += step;
}
if ((delta & 2) != 0) {
vpdiff += step >> 1;
}
if ((delta & 1) != 0) {
vpdiff += step >> 2;
}
if (sign != 0) {
valpred -= vpdiff;
} else {
valpred += vpdiff;
}
if (valpred > 32767)
valpred = 32767;
else if (valpred < -32768)
valpred = -32768;
step = stepsizeTable[index];
fos.write(Hex2ByteDTX(valpred));
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static byte[] Hex4ByteDTX(Long l) {
int len = 4;
byte[] b = new byte[len];
for (int i = 0; i < len; i++) {
b[i] = (byte) (l >> (i * 8) & 0x00ff);
}
return b;
}
private static byte[] Hex2ByteDTX(int l) {
int len = 2;
byte[] b = new byte[len];
for (int i = 0; i < len; i++) {
b[i] = (byte) (l >> (i * 8) & 0x00ff);
}
return b;
}
private static void writeHeard(long length) {
long nBlock = length / 256;
long dataSize=nBlock*505;
long otherBytes = length % 256;
if (otherBytes != 0) {
dataSize +=(otherBytes-4)*2+1;
}
riff = new RIFF();
format = new FORMAT("PCM");
data = new DATA();
data.setDATASize(dataSize * 2);
riff.setRIFFSize(data.getDATASize() + 36);
try {
fos.write(riff.getRIFFID().getBytes());
fos.write(Hex4ByteDTX(riff.getRIFFSize()));
fos.write(riff.getRIFFType().getBytes());
fos.write(format.getFORMATID().getBytes());
fos.write(Hex4ByteDTX(format.getFORMATSize()));
fos.write(Hex2ByteDTX(format.getFORMATAudioFormat()));
fos.write(Hex2ByteDTX(format.getFORMATNumChannels()));
fos.write(Hex4ByteDTX(format.getFORMATSampleRate()));
fos.write(Hex4ByteDTX(format.getFORMATByteRate()));
fos.write(Hex2ByteDTX(format.getFORMATBlockAlign()));
fos.write(Hex2ByteDTX(format.getFORMATBitsPerSample()));
fos.write(data.getDATAID().getBytes());
fos.write(Hex4ByteDTX(data.getDATASize()));
} catch (IOException e) {
e.printStackTrace();
}
}
}