wangtianbao

pcm转wav

PCM转WAV只需在PCM文件前面增加WAV头即可,WAV文件头中包含的信息有:采样率、采样的位数、文件大小等。头文件的长度占44字节。

采样的位数指的是描述数字信号所使用的位数。8位(8bit)代表2的8次方=256,16 位(16bit)则代表2的16次方=65536 / 1024 =64K

采样率是一秒钟内对声音信号的采样次数

网络接收一个音频的时长是20ms, 已知音频采样率是8kHz,采样的位数是16bit。
[时长]20ms * [采样率]8kHz * [采样的位数]16bit = 320 byte

例如,CD碟采用16位的采样精度,44.1KHz的采样频率,为双声道,它每秒所需要的数据量为16×44100×2÷8=176400字节。这样算下来,比特率应该是1400多Kbps,如果采用MP3、WMA编码格式,比特率能够更小。


WAV文件的基本格式
类型内容变量名大小取值
RIFF头 文件标识符串 fileId 4B "RIFF"
头后文件长度 fileLen 4B 非负整数(=文件长度-8)
数据类型标识符 波形文件标识符 waveId 4B "WAVE"
格式块 块头 格式块标识符 chkId 4B "fmt"
头后块长度 chkLen 4B 非负整数(=16或18)
块数据 格式标记 wFormatTag 2B 非负短整数(PCM=1)
声道数 wChannels 2B 非负短整数(=1或2)
采样率 dwSampleRate 4B 非负整数(单声道采样数/秒)
平均字节率 dwAvgBytesRate 4B 非负整数(字节数/秒)
数据块对齐 wBlockAlign 2B 非负短整数(不足补零)
采样位数 wBitsPerSample 2B 非负短整数(PCM时才有)
扩展域大小 wExtSize 2B 非负短整数 可选(根据chkLen=16或18判断)
扩展域 extraInfo extSize B 扩展信息
数据块 块头 数据块标识符串 chkId 4B "data"
头后块长度 chkLen 4B 非负整数
块数据 波形采样数据 x 或 xl、xr chkLen B 左右声道样本交叉排列
样本值为整数(整字节存储,不足位补零),整个数据块按blockAlign对齐


示例:
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);
            // 计算长度
            int pcmSize = fis.available();// 由于音频文件一般不会大于 Int 最大值,所以使用 available
            
// 填入参数,比特率等等。这里用的是 16位 单声道 8000 hz
            WavHeader header = new WavHeader();
            // 长度字段 = 内容的大小(PCMSize) + 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)
            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标准,头部应该是44字节
            
// 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("执行["+methodName+"]方法异常!对象:"+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("关闭资源异常!对象:"+obj+";异常:"+e.getMessage(), e);
            }
        }
    }
    
}

package com.pvcp.common.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class WavHeader {

    /**
     * 4 资源交换文件标志(RIFF)
     
*/
    public final char fileID[] = { 'R', 'I', 'F', 'F' };
    /**
     * 4 总字节数
     
*/
    public int fileLength;
    /**
     * 4 WAV文件标志(WAVE)
     
*/
    public char wavTag[] = { 'W', 'A', 'V', 'E' };
    /**
     * 4 波形格式标志(fmt ),最后一位空格
     
*/
    public char fmtHdrID[] = { 'f', 'm', 't', ' ' };
    /**
     * 4 过滤字节(一般为00000010H),若为00000012H则说明数据头携带附加信息
     
*/
    public int fmtHdrLeth;
    /**
     * 2 格式种类(值为1时,表示数据为线性PCM编码)
     
*/
    public short formatTag;
    /**
     * 2 通道数,单声道为1,双声道为2
     
*/
    public short channels;
    /**
     * 4 采样频率
     
*/
    public int samplesPerSec;
    /**
     * 4 波形数据传输速率(每秒平均字节数)
     
*/
    public int avgBytesPerSec;
    /**
     * 2 DATA数据块长度,字节
     
*/
    public short blockAlign;
    /**
     * 2 PCM位宽
     
*/
    public short bitsPerSample;
    /**
     * 4 数据标志符(data)
     
*/
    public char dataHdrID[] = { 'd', 'a', 't', 'a' };
    /**
     * 4 DATA总数据长度字节
     
*/
    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);
        }
    }

}

posted on 2018-05-08 10:02 王添宝 阅读(66) 评论(0)  编辑  收藏


只有注册用户登录后才能发表评论。


网站导航: