首页 > 编程知识 正文

怎么修改音频格式为WAV,音频文件怎么更改为wav格式

时间:2023-05-03 22:28:48 阅读:250692 作者:2611

前 言

记得之前写过一篇文章,介绍怎么将amr音频转为wav格式,这个过程是没有问题的,转码产生的音频文件是可以正常播放的。但是,由于项目中的服务器智能播放比特率为64kbps的wav音频,而转码产生的wav音频比特率为128kbps,导致不可用。

wav音频

WAVE文件作为多媒体中使用的声波文件格式之一,它是以RIFF格式为标准的。RIFF是英文Resource Interchange File Format的缩写,每个WAVE文件的头四个字节便是“RIFF”。

技术选型

由于转码产生的音频比特率不符合要求,那么就需要修改wav文件的比特率为64kbps了。该音频的采样率为16k,改为8k就可以。

构建一个wav文件头

wav文件头是有一定结构的,占用44个字节记录文件的信息。

public class PcmWavHead { /** * 0-3个字节,WAV固定为RIFF,RIFF是windows下的一种常用多媒体格式存储标准 */ public String ChunkID="RIFF"; /** * 4-7个字节,文件长度 */ public int ChunkSize=0; /** * 8-11个字节,WAV文件格式标志 */ public String Format="WAVE"; /** * 12-15个字节,"fmt "标志 */ public String SubChunk1ID="fmt "; /** * 16-19个字节 块大小,初始化为一个数,但是实际上的大小决定,计算的公式为 (HeadSize + DataSize - 8) = (44-8+DataSize) 字节 */ public int SubChunk1Size=0x10; // /** * 20-21个字节,格式类别(0x01H为PCM形式的声音数据) */ public int AudioFormat=0x1; // /** * 22-23个字节, 通道数,单声道为1,双声道为2 */ public int NumChannels=1; // /** * 24-27个字节 采样率(每秒样本数),表示每个通道的播放速度,8000 | 6000 | 11025 | 16000 */ public int SampleRate=16000; // /** * 28-31个字节 波形音频数据传送速率,其值Channels×SamplesPerSec×BitsPerSample/8 每秒字节数 */ public int ByteRate=32000; //32Kbps /** * 32-33个字节,数据块的调整数(按字节算的),其值为Channels×BitsPerSample/8 */ public int BlockAlign=2; // /** * 34-35个字节 每样本的数据位数,表示每个声道中各个样本的数据位数。如果有多个声道,对每个声道而言,样本大小都一样。量化比特数: 8 | 16 */ public int BitsPerSample=16; // /** * 36-39个字节,数据标记符"data",前11个是用于鉴别文件的头部信息 */ public String DataTag="data"; // /** * 40-43个字节,语音数据的长度(文长-44) */ public int DataSize=0; // /** * 固定头信息文长44个字节 */ public static int HeadSize=44;} 读取wav的头结构 /*** 从data流中读取文件头** @param dis 输入数据流* @return void*/public void readHead(DataInputStream dis) { byte[] byteHeads= new byte[HeadSize]; try { dis.readFully(byteHeads, 0, HeadSize);//读取pcm文件的前44个字节,保存到byteHeads当中 } catch (IOException e) { logger.error(ExceptionTool.getExceptionStacksMessage(e)); } readHead(byteHeads);}/*** 从文件流中读取文件头** @param fis 输入数据流* @return void*/public void readHead(FileInputStream fis) { byte[] byteHeads= new byte[HeadSize]; try { fis.read(byteHeads, 0, HeadSize); } catch (IOException e) { e.printStackTrace(); } readHead(byteHeads);}/*** 将保存了头信息的44个字节,装载到类当中** @param pcmHead 保存了pcm文件的前44个字节的数组* @return void*/public void readHead(byte[] pcmHead){ //11个关键文件头字段 ChunkID = SpeexUtil.readString(pcmHead, 0, 4); ChunkSize = SpeexUtil.readInt(pcmHead, 4); Format = SpeexUtil.readString(pcmHead, 8, 4); SubChunk1ID = SpeexUtil.readString(pcmHead, 12, 4); SubChunk1Size = SpeexUtil.readInt(pcmHead, 16); AudioFormat = SpeexUtil.readShort(pcmHead, 20);// NumChannels = SpeexUtil.readShort(pcmHead, 22);// SampleRate = SpeexUtil.readInt(pcmHead, 24); ByteRate = SpeexUtil.readInt(pcmHead, 28); BlockAlign = SpeexUtil.readShort(pcmHead, 32);// BitsPerSample = SpeexUtil.readShort(pcmHead, 34);// DataTag = SpeexUtil.readString(pcmHead, 36, 4); DataSize = SpeexUtil.readInt(pcmHead, 40);}

SpeexUtil中的方法如下:

/*** 使用Int的方式读取数据流中数据,保存在byte数组当中** @param data 用于保存读取信息的byte数组* @param offset 偏置第几个字节* @return int*/public static int readInt(final byte[] data, final int offset) { /* * no 0xff on the last one to keep the sign */ return (data[offset] & 0xff) | ((data[offset + 1] & 0xff) << 8) | ((data[offset + 2] & 0xff) << 16) | (data[offset + 3] << 24);}/*** 使用short的方式读取数据流中数据,保存在byte数组当中** @param data 用于保存读取信息的byte数组* @param offset 偏置第几个字节* @return short*/public static int readShort(final byte[] data, final int offset) { /* * no 0xff on the last one to keep the sign */ return (data[offset] & 0xff) | (data[offset + 1] << 8);}/*** 使用String的方式读取数据流中数据,保存在byte数组当中** @param data 用于保存读取信息的byte数组* @param offset 偏置第几个字节* @return String*/public static String readString(final byte[] data, final int offset, final int len) { return new String(data, offset, len);} 读取文件字节数组 int totalpcmsize=pcmWavHead.DataSize;// 跳过44个字节int wavLen= (int) (totalpcmsize)/2; // 输入数据长度int rawLen= wavLen/2; // 输出数据长度short[] wavData=new short[wavLen]; // 输入数据数组short[] rawData = new short[ rawLen] ; // 输出数据数组byte[] buffer1=new byte[totalpcmsize];dis.read(buffer1, 0, totalpcmsize);//将字节拼成shortwavData=byteArray2ShortArray(buffer1, wavLen); 将字节数组转为short数组 /*** 将byte型的数组,转换为short型的数组** @param data* @param items* @return short[]*/public static short[] byteArray2ShortArray(byte[] data, int items) { short[] retVal =new short[items]; for (int i =0; i < retVal.length; i++) retVal[i] = (short) ((data[i *2]&0xff) | (data[i *2+1]&0xff) <<8); return retVal;} 修改文件采样率为8k

采样率变了之后,数据大小也会跟着变

计算 Pcm的时长:时长=数据量byte/(采样率*(采样位数/8)*声道数)

pcmWavHead.SampleRate=8000;pcmWavHead.DataSize=wavLen;// (int) m_duration * (pcmWavHead.SampleRate*(16/8)*1);byte[] headBytes=pcmWavHead.buildHeader();//将头文件写入新wav文件outputStream.write(headBytes, 0, PcmWavHead.HeadSize);/*** 根据当前类的实例,构建的一个pcm头,并以byte数组的形式返回** @param* @return byte[] byte数组的形式返回的头信息*/public byte[] buildHeader() { byte[] header = new byte[HeadSize]; SpeexUtil.writeString(header, 0, "RIFF"); SpeexUtil.writeInt (header, 4, DataSize+HeadSize-8);//总体长度减去8 SpeexUtil.writeString(header, 8, "WAVE"); SpeexUtil.writeString(header, 12, "fmt "); SpeexUtil.writeInt (header, 16, 0x10); // Size of format chunk SpeexUtil.writeShort (header, 20, (short) 0x01); // Format tag: PCM SpeexUtil.writeShort (header, 22, (short) NumChannels); // Number of channels SpeexUtil.writeInt (header, 24, SampleRate); // Sampling frequency SpeexUtil.writeInt (header, 28, SampleRate*NumChannels * (BitsPerSample/8)); // Average bytes per second SpeexUtil.writeShort (header, 32, (short) NumChannels * (BitsPerSample/8)); // Blocksize of data SpeexUtil.writeShort (header, 34, (short) BitsPerSample); // Bits per sample SpeexUtil.writeString(header, 36, "data"); SpeexUtil.writeInt (header, 40, DataSize); // Data Size return header;} 抽取数据转为8k //抽取数据 将16k转为 8Kfor(int i=0;i<rawLen-1;i++){ rawData[i]=(short) ((wavData[i*2] + wavData[(i+1)*2])/2);}for (int i = 0; i < rawLen-1; i++) { //写数据,不减1 数组会越界why? writeShort(outputStream, rawData[i]);} 总 结

转化wav音频的转化率还是比较复杂的,涉及到底层数组的的操作。原理就是将文件头保存的采样率16k改为8k,再计算文件的大小,并将文件数据转化为8k。这个过程还是需要对wav文件的结构熟悉。总之,这个部分还是很复杂的,我现在还只是知道个大概。今天将这个知识点写出来,就是想将自己的思考整理一下吧。

ULB服务器是否会宕机? 负载均衡 ULBJS前端首屏优化技巧SSM框架怎么配置springmvc-config.xml文件

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。