WAV格式文件无损合并&帧头数据体解析(python)(原创)

2023-02-18,,,,

一,百度百科

  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格式文件所占容量(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大子块,每个子块又分为若干功能块。有标志位、数据长度、通道数、采样率等等相关参数。

 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)(原创)的相关教程结束。

《WAV格式文件无损合并&帧头数据体解析(python)(原创).doc》

下载本文的Word格式文档,以方便收藏与打印。