前 言

记得之前写过一篇文章,介绍怎么将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);//将字节拼成short
wavData=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);//总体长度减去8SpeexUtil.writeString(header,  8, "WAVE");SpeexUtil.writeString(header, 12, "fmt ");SpeexUtil.writeInt   (header, 16, 0x10);                    // Size of format chunkSpeexUtil.writeShort (header, 20, (short) 0x01);          // Format tag: PCMSpeexUtil.writeShort (header, 22, (short) NumChannels);      // Number of channelsSpeexUtil.writeInt   (header, 24, SampleRate);            // Sampling frequencySpeexUtil.writeInt   (header, 28, SampleRate*NumChannels * (BitsPerSample/8)); // Average bytes per secondSpeexUtil.writeShort (header, 32, (short) NumChannels * (BitsPerSample/8));    // Blocksize of dataSpeexUtil.writeShort (header, 34, (short) BitsPerSample);            // Bits per sampleSpeexUtil.writeString(header, 36, "data");SpeexUtil.writeInt   (header, 40, DataSize); // Data Sizereturn header;
}

抽取数据转为8k

//抽取数据 将16k转为 8K
for(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文件的结构熟悉。总之,这个部分还是很复杂的,我现在还只是知道个大概。今天将这个知识点写出来,就是想将自己的思考整理一下吧。

修改wav格式音频比特率的标准方法相关推荐

  1. python按固定采样点个数分割wav格式音频

    最近开始做实验需要绘制音频的语谱图,绘制语谱图的过程中需要FFT过程,FFT需要采样点个数是2的整数倍,所以为了生成语谱图的大小合适,那么总长65536是个比较合适的数,对于采样率32kHz的wav音 ...

  2. c语言 输出音频 单片机,单片机播放WAV格式音频的理解

    CSDN账号注册了3年,一直没有上来过,更不用说写博客了.我不知道博客的具体用途,我只想把它当做一种心得来发表,可能是一些技术上的理解或者生活上的小故事.好了,下面我将记录我对WAV播放器的理解. 很 ...

  3. 单片机播放WAV格式音频的理解

    CSDN账号注册了3年,一直没有上来过,更不用说写博客了.我不知道博客的具体用途,我只想把它当做一种心得来发表,可能是一些技术上的理解或者生活上的小故事.好了,下面我将记录我对WAV播放器的理解. 很 ...

  4. matlab wav格式音频去除人声

    matlab wav格式音频去除人声(原理自查) 先设立Hbs带阻函数(matlab2018a为例) 选择右上view可以查看函数效果如下 应用函数 代码块 代码块语法遵循标准markdown代码,例 ...

  5. 【音视频数据数据处理 10】【PCM篇】将PCM转为WAV格式音频

    [音视频数据数据处理 10][PCM篇]将PCM转为WAV格式音频 一.WAV头信息 1.1 RIFF区块 1.2 FORMAT区块 1.3 DATA区块 二.PCM 转 WAV 代码实现 PCM转为 ...

  6. wav格式音频怎么转换mp3

    对于一些音乐的爱好者们来说音频格式运用的很广发,大家是否发现有这样一个问题,每次自己在电脑上下载的歌曲最后在手机或者其他设备上准备播放的时候都会显示无法播放,你们有找过这是什么原因造成的吗?小编今天告 ...

  7. TTS生成wav格式音频添加报头信息

    TTS生成wav格式音频添加报头信息 由于使用百度语音或是科大讯飞语音TTS合成的音频文件,内部没有写入音频的关键信息,如采样率,码率,通道数,位数等信息.在linux下,使用aplay命令无法播放此 ...

  8. (原创)speex与wav格式音频文件的互相转换(二)

    之前写过了如何将speex与wav格式的音频互相转换,如果没有看过的请看一下连接 http://www.cnblogs.com/dongweiq/p/4515186.html 虽然自己实现了相关的压缩 ...

  9. 如何把WAV格式音频转换为MP3格式

    WAV为微软公司(Microsoft)开发的一种声音文件格式,它符合RIFF(Resource Interchange File Format)文件规范,用于保存Windows平台的音频信息资源,被W ...

  10. (原创)speex与wav格式音频文件的互相转换

    我们的司信项目又有了新的需求,就是要做会议室.然而需求却很纠结,要继续按照原来发语音消息那样的形式来实现这个会议的功能,还要实现语音播放的计时,暂停,语音的拼接,还要绘制频谱图等等. 如果是wav,m ...

最新文章

  1. python中formatter的用法_sql-formatter 格式化sql
  2. 《网络攻防实践》第二周学习总结
  3. 出现了错误。详细消息: 3 uncommitted changes would be overwritten by merge
  4. 直播 | COLING 2020 论文解读:基于话题引导的对话推荐系统
  5. python基础—循环基础知识点
  6. 用PowerDesigner工具条不见的
  7. Machine Learning - Coursera week6 Evaluating a learning algorithm
  8. 新独立版抖音口红机全修复版本附视频教程
  9. cte公用表表达式_CTE SQL删除; 在SQL Server中删除具有公用表表达式的数据时的注意事项
  10. mysql将俩个SQL查询出来的不一样的结果横向拼接成一行数据
  11. SQL SERVER2000教程-第五章 处理数据 第二节 检索数据
  12. 杨辉三角形Java实现
  13. RC时间常数 积分微分 耦合
  14. 浪漫七夕—很幸运一路有你
  15. Android自定义控件(高手级)--JOJO同款能力分析图,这份字节跳动历年校招Android面试真题解析
  16. 【高德地图进阶】--- 带图片的点(3)
  17. C++程序员专用表白程序让你度过一个美妙的七夕节
  18. java poi写入excel_Java使用POI读取和写入Excel指南
  19. Android中 根据电话号码获取联系人姓名
  20. ActiveX控件打包cab时INF文件的编写

热门文章

  1. 终于有一款视频社交应用打破微信的榜首垄断了,Faceu再登Appstore总榜第一
  2. PINN解偏微分方程--程函方程
  3. java.util.ConcurrentModificationException: null 报错解决
  4. python concat_python数据拼接: pd.concat
  5. 打开 igv java_IGV加载很久很烦人?三步帮你解决!
  6. Kteer软件 创建.ktr文件
  7. 如何从官网直接下载iTunes?
  8. 为中国的孩子制造安全的校车
  9. 删除Linux中的.swp文件
  10. Slove the {Failed to load unit 'HGCM' (VERR_INVALID_PARAMETER)}