一, 百度百科
WAV 为微软公司 (Microsoft) 开发的一种声音文件格式, 它符合 RIFF(Resource Interchange File Format)文件规范, 用于保存 Windows 平台的音频信息资源, 被 Windows 平台及其应用程序所广泛支持, 该格式也支持 MSADPCM,CCITT A LAW 等多种压缩运算法, 支持多种音频数字, 取样频率和声道, 标准格式化的 WAV 文件和 CD 格式一样, 也是 44.1K 的取样频率, 16 位量化数字, 因此在声音文件质量和 CD 相差无几! WAV 打开工具是 Windows 的媒体播放器.
通常使用三个参数来表示声音, 量化位数, 取样频率和采样点振幅. 量化位数分为 8 位, 16 位, 24 位三种, 声道有单声道和立体声之分, 单声道振幅数据为 n*1 矩阵点, 立体声为 n*2 矩阵点, 取样频率一般有 11025Hz(11kHz) ,22050Hz(22kHz)和 44100Hz(44kHz) 三种, 不过尽管音质出色, 但在压缩后的文件体积过大! 相对其他音频格式而言是一个缺点, 其文件大小的计算方式为: WAV 格式 https://baike.baidu.com/item/WAV格式 文件所占容量(B) = (取样频率 X 量化位数 X 声道) X 时间 / 8 (字节 = 8bit) 每一分钟 WAV 格式的音频文件的大小为 10MB, 其大小不随音量大小及清晰度的变化而变化.
WAV 是最接近无损的音乐格式, 所以文件大小相对也比较大.
二, 文件帧头
图 1 WAV 文件帧头 data[0:44]数据格式
图 2.WAV 文件帧头图解
读取 WAV 文件程序:
- import struct
- with open('测试音频源 1.wav', 'rb') as file:
- data=file.read()
- # print(len(data))
- # print(data[44:])
- # print(data[0:4]) # chunkID: b'RIFF'
- # length0=struct.unpack('<L', bytes(data[4:8]))
- # print(length0) # (140836,)
- # print(data[4:8]) # chunkSize: b'$&\x02\x00' WAV 文件总 byte 数
- # print(data[8:12]) # format: b'WAVE'
- # print(data[12:16]) # Subchunk1 ID: b'fmt'
- # length1=struct.unpack('<L', bytes(data[16:20]))
- # print(length1) # (16,)
- # print(data[16:20]) # format Code: b'\x10\x00\x00\x00'
- #
- # print(data[20:22]) # Subchunk1 Size: b'\x01\x00'
- # print(data[22:24]) # nChannels: b'\x01\x00'
- #
- # print(data[24:28]) # nSamplesPerSec: b'\x80>\x00\x00'
- # print(data[28:32]) # nAvgBytesPerSec: b'\x00}\x00\x00'
- #
- # print(data[32:34]) # nBlockAlign: b'\x02\x00'
- # print(data[34:36]) # wBitsPerSample: b'\x10\x00'
- #
- # print(data[36:40]) # Subchunk2 ID: b'data'
- # length2=struct.unpack('<L', bytes(data[40:44])) # (140800,)
- # print(length2)
- # print(data[40:44]) # Subchunk2 Size: b'\x00&\x02\x00'
通过将 data 值输出, 可知其是一个 byte 文件
帧头数据为 data[0:44], 例如:
其中又划分出 3 大子块, 每个子块又分为若干功能块. 有标志位, 数据长度, 通道数, 采样率等等相关参数.
1 b'RIFF\xac\xdc9\x00WAVEfmt\x10\x00\x00\x00\x01\x00\x01\x00\x80>\x00\x00\x00}\x00\x00\x02\x00\x10\x00data\x80\xdc9\x00'
数据帧为 data[44:], 剩余的数据即为音频采样数据.
三, WAV 文件无损合并
我这种方法只针对通道数, 采样率等等 (除了文件数据帧长度不同) 都相同的多个 WAV 文件合并, 当然如果想要将不同格式的 WAV 合并也可以先转换成相同格式的文件之后再做操作.
- import struct # 用于将 chunkSize 和 Subchunk2 Size 进行 [long int] (byte 型) 和 int 的转换
- # *** 读取 WAV 音频 1 *** #
- with open('测试音频源 1.wav', 'rb') as file:
- data1=file.read()
- # *** 读取 WAV 音频 2 *** #
- with open('测试音频源 2.wav', 'rb') as file:
- data2=file.read()
- data_info = data1[:44] # 复制帧头参考
- data_out = data1[44:] + data2[44:] # 将两个音频的数据帧合并(都是相同格式)
- data_info = data_info[:4] + struct.pack('<L', len(data_out)+44) + data_info[8:]# 更新 WAV 文件的总 byte 数(两个文件数据帧和 + 44)
- data_info = data_info[:40] + struct.pack('<L', len(data_out)) + data_info[44:]# 更新 WAV 文件的数据 byte 数(两个文件数据帧和)
- # *** 生成合并后的 WAV 文件 *** #
- with open('测试音频源 3.wav', 'wb') as f:
- f.write(data_info+data_out)
- print('完成')
四, 常见问题
我之前遇到的问题, 直接将两个文件的 byte 值相加写入新文件, 帧头没有更改; 这样写的结果就是数据的大小满足两个源文件的和, 但是使用播放器播放的时候音频无法正常全部播放.
尤其是我使用阿里云 - 语音合成 API 合成的 WAV 格式音频, 它们的格式有一定的问题, 每个生成的 chunkSize 和 Subchunk2 Size 数值都比实际音频数据长度要大一些, 导致我直接将多个音频合并的时候, 音频长度超过一定长度, 后面的语音就无法播放, 但是较少的几段音频合并又可以正常播放, 这个地方我一直都没有弄明白, 同时我又不想使用第三方的库(主要是觉得要先将音频存起来 - 之后又读取很麻烦), 所以才细心的参看 WAV 格式文件的相关资料, 通过对多个音频的比对发现了这个问题的由来.
备注: 如果想要直接使用 byte 文件进行 WAV 文件合并一定要在合并后更新相关的数据, 与此同时也要注意文件的通道数, 采样频率等格式是否相同, 一定要转换到相同格式合并才有效
WAV 格式文件合并 & 帧头数据体解析(python)(原创)
来源: http://www.bubuko.com/infodetail-3061575.html