关于ADPCM编码和PCM编码的.wav文件通过Java进行相互转换
最近在做一个呼叫器的项目,中间碰到了点问题,就是呼叫器那边传过来的数据是ADPCM编码格式的,经过程序转换成.wav文件后可以在播放器上进行播放,但是无法在浏览器上进新播放。在网上查资料的时候,几乎都是C语言的版本,在程序中无法使用,就参考C语言用Java写了个编解码程序。
对.wav文件做编码转换,就需要对wav文件的格式进行了解,网上有很多wav文件格式解析的文章。我总结了一下,做了一个表格。
代码里面有些重复的,没有进行优化缩减,用到的可以自行优化一下
wav头中的RIFF块
/*** @Description TODO* @Author liminghui* @Date /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 /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 /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 /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 /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 /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和rsvblockheard.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;elsevalpred += 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 /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\\-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才为1sign = 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();}}}
如果觉得《关于ADPCM编码和PCM编码的wave文件通过Java进行相互转换》对你有帮助,请点赞、收藏,并留下你的观点哦!