一,百度百科
圖1 WAV文件幀頭data[0:44]數據格式
圖2.WAV文件幀頭圖解
讀取WAV文件程序:
1 import struct 2 3 with open('測試音頻源1.wav', 'rb') as file: 4 data=file.read() 5 # print(len(data)) 6 # print(data[44:]) 7 # print(data[0:4]) # chunkID: b'RIFF' 8 # length0=struct.unpack('<L', bytes(data[4:8])) 9 # print(length0) # (140836,) 10 # print(data[4:8]) # chunkSize: b'$&\x02\x00' WAV文件總byte數 11 # print(data[8:12]) # format: b'WAVE' 12 # print(data[12:16]) # Subchunk1 ID: b'fmt ' 13 # length1=struct.unpack('<L', bytes(data[16:20])) 14 # print(length1) # (16,) 15 # print(data[16:20]) # format Code: b'\x10\x00\x00\x00' 16 # 17 # print(data[20:22]) # Subchunk1 Size: b'\x01\x00' 18 # print(data[22:24]) # nChannels: b'\x01\x00' 19 # 20 # print(data[24:28]) # nSamplesPerSec: b'\x80>\x00\x00' 21 # print(data[28:32]) # nAvgBytesPerSec: b'\x00}\x00\x00' 22 # 23 # print(data[32:34]) # nBlockAlign: b'\x02\x00' 24 # print(data[34:36]) # wBitsPerSample: b'\x10\x00' 25 # 26 # print(data[36:40]) # Subchunk2 ID: b'data' 27 # length2=struct.unpack('<L', bytes(data[40:44])) # (140800,) 28 # print(length2) 29 # 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合並也可以先轉換成相同格式的文件之后再做操作。
1 import struct # 用於將chunkSize和Subchunk2 Size進行【long int】(byte型)和 int的轉換 2 3 # *** 讀取WAV音頻1 *** # 4 with open('測試音頻源1.wav', 'rb') as file: 5 data1=file.read() 6 7 # *** 讀取WAV音頻2 *** # 8 with open('測試音頻源2.wav', 'rb') as file: 9 data2=file.read() 10 11 data_info = data1[:44] # 復制幀頭參考 12 data_out = data1[44:] + data2[44:] # 將兩個音頻的數據幀合並(都是相同格式) 13 data_info = data_info[:4] + struct.pack('<L', len(data_out)+44) + data_info[8:]# 更新WAV文件的總byte數(兩個文件數據幀和+44) 14 data_info = data_info[:40] + struct.pack('<L', len(data_out)) + data_info[44:]# 更新WAV文件的數據byte數(兩個文件數據幀和) 15 16 # *** 生成合並后的WAV文件 *** # 17 with open('測試音頻源3.wav', 'wb') as f: 18 f.write(data_info+data_out) 19 20 print('完成')
四,常見問題
我之前遇到的問題,直接將兩個文件的byte值相加寫入新文件,幀頭沒有更改;這樣寫的結果就是數據的大小滿足兩個源文件的和,但是使用播放器播放的時候音頻無法正常全部播放。
尤其是我使用阿里雲-語音合成api合成的WAV格式音頻,它們的格式有一定的問題,每個生成的chunkSize和Subchunk2 Size數值都比實際音頻數據長度要大一些,導致我直接將多個音頻合並的時候,音頻長度超過一定長度,后面的語音就無法播放,但是較少的幾段音頻合並又可以正常播放,這個地方我一直都沒有弄明白,同時我又不想使用第三方的庫(主要是覺得要先將音頻存起來-之后又讀取很麻煩),所以才細心的參看WAV格式文件的相關資料,通過對多個音頻的比對發現了這個問題的由來。
備注:如果想要直接使用byte文件進行WAV文件合並一定要在合並后更新相關的數據,與此同時也要注意文件的通道數、采樣頻率等格式是否相同,一定要轉換到相同格式合並才有效