【轉】Python處理wave文件
#本文PDF版下載
Python解析Wav文件並繪制波形的方法
#本文代碼下載
Wav波形繪圖代碼
#本文實例音頻文件night.wav下載
音頻文件下載 (石進-夜的鋼琴曲)
前言
在現在繁忙的生活中,我們經常會聽些歌來放松一下自己,我們經常會從各種播放軟件中聽自己喜歡的歌,並且往往我們會下載一部分歌曲,而現在音頻的種類也相當繁多,像是Wav,Mp3,FLAC,AAC等等很多格式,最近由於需要做一個能夠分析Wav格式音頻的波形來取得一些數據比如獲取人錄音時是否說完等等用途.本周先對解析Wav並用Python繪制其波形進行了一些探索.
Wav文件格式
我們先來看看wikipedia上對於wav音頻格式的解釋:
「Waveform Audio File Format(WAVE,又或者是因為擴展名而被大眾所知的WAV),是微軟與IBM公司所開發在個人電腦存儲音頻流的編碼格式,在Windows平臺的應用軟件受到廣泛的支持,地位上類似於麥金塔電腦裏的AIFF.此格式屬於資源交換檔案格式(RIFF)的應用之一,通常會將采用脈沖編碼調制的音頻資存儲在區塊中。也是其音樂發燒友中常用的指定規格之一.由於此音頻格式未經過壓縮,所以在音質方面不會出現失真的情況,但檔案的體積因而在眾多音頻格式中較為大.」
我們可以看到上面提到了兩個個關鍵詞RIFF和脈沖編碼調制.所以我們接下來先解釋一下 RIFF「資源交換檔案格式」是什麽.
RIFF格式
我們同樣的來看一下wikipedia對RIFF的解釋
「 Resource Interchange File Format(簡稱RIFF),資源交換文件格式,是一種按照標記區塊存儲數據(tagged chunks)的通用文件存儲格式,多用於存儲音頻、視頻等多媒體數據.Microsoft在windows下的AVI、ANI 、WAV等都是基於RIFF實現的.
RIFF是由Microsoft和IBM於1991年,在windows 3.1中引入的,作為windows 3.1默認的多媒體文件格式。RIFF是參考Interchange File Format來的,二者主要的區別是字節序大端、小端的問題.在基於IBM的80x86系列主機下,RIFF的字節序是小端的;而在IFF原有的格式中是按照大端存儲整型數據的.」
RIFF是由chunk構成的,chunk是RIFF組成的基本單位,每個CHUNK可看作存貯了視頻的一幀數據或者是音頻的一幀數據,所以下面我們來討論一下chunk的結構是怎麽樣的.
CHUNK的結構
CHUNK總共由三個部分組成:
- FOURCC 使用4字節的ASIIC字符標識類型
- SIZE 數據的大小
- DATA 用於存放數據
結構示意圖如下:
CHUNK的結構
- CHUNK在一般情況下不能嵌套,但是當CHUNK的FOURCC為“RIFF”或者是“LIST”的時候可以嵌套數據.
- “RIFF”的第一個CHUNK的FOURCC一定是“RIFF”,所以LIST為FOURCC的一定是子CHUNK及SUBCHUNK.
下面是一個包含了子CHUNK的結構示意圖:
包含了SUBCHUNK的結構示意圖
- “RIFF”的CHUNK在DATA區域的前四個字節稱為“Form Type”記錄了數據的類型,比如我們的wav文件的Form Type就是“WAV”
Form Type結構示意圖如下:
包含了Form Type的結構示意圖
- 同樣的FOURCC為LIST的 SUBCHUNK 的DATA區域也包含了LIST Type,用於表示LIST中數據區域格式
脈沖編碼調制(PCM)
我們先來看看百度百科對它的解釋:
「PCM 脈沖編碼調制是Pulse Code Modulation的縮寫,脈沖編碼調制是數字通信的編碼方式之一.主要過程是將話音、圖像等模擬信號每隔一定時間進行取樣,使其離散化,同時將抽樣值按分層單位四舍五入取整量化,同時將抽樣值按一組二進制碼來表示抽樣脈沖的幅值.」
我們從上面的介紹可以理解為:
通過三個過程-抽樣、量化和編碼講音頻的模擬信號轉化為數字信號.
抽樣
抽樣是由於模擬信號是連續的,通過一定頻率對模擬信號進行取樣,近似得到,如下面的圖中灰色框中就是取了一定頻率進行抽樣的示意圖:
抽樣過程示意圖
- 抽樣是要將模擬信號以其信號帶寬2倍以上的頻率提取樣值,變為在時間軸上*離散*的抽樣信號,就可獲得能取代原來連續音頻信號的抽樣信號.對一個正弦信號進行抽樣獲得的抽樣信號是一個脈沖幅度調制(PAM)信號,之後對抽樣信號進行檢波和平滑濾波,即可還原出原來的模擬信號.
量化
抽樣信號離散的模擬信號,其取樣的值在一定的取值範圍內,由無限多種值可能性存在.為了實現以數字碼表示樣值,我們采用“四舍五入”的方法把樣值分級“取整”,使一定取值範圍內的樣值由無限多個值變為有限個值.
編碼
量化後的抽樣信號在一定的取值範圍內僅有有限個可取的樣值,且信號正、負幅度分布的對稱性使正、負樣值的個數相等,正、負向的量化級對稱分布
WAV文件的CHUNK信息
WAVE文件是由若幹個CHUNK組成的.按照文件中CHUNK的出現順序分別為:RIFF Chunk, Format Chunk, Fact Chunk, Data Chunk,其中的Fact CHUNK為非必要部分,結構具體如下圖所示:
WAV文件頭文件的CHUNK組成
RIFF是頭CHUNK,而Format CHUNK裏面記錄了WAV的各種參數信息,詳細參數信息如下:
- FormatTag 音頻數據的編碼方式,其中PCM方式為1
- Channels 聲道數,單聲道為1,雙聲道為2
- SamplesPerSec 采樣率(每秒樣本數)
- BytesPerSec* 音頻數據傳送速率
- BlockAlign* 每次采樣的大小
- BitsPerSample* 每個聲道的采樣精度
而FACT CHUNK的作用是因為有些並沒有使用PCM格式,所以需要一個FACT CHUNK記錄數據解壓縮數據大小.
最後的DATA塊中裝的是真正的聲音數據.一般按照WAVE_FORMAT_PCM的數據格式存貯,即脈沖編碼調制PCM.
WAV的DATA部分
DATA塊內的信息是根據format chunk內的信息而決定的.由量化位數/聲道數/采樣率共同決定,下圖為四種情況下DATA區域存儲信息的格式:
DATA CHUNK的格式
Python讀取WAV文件的信息
在python中,我們可以直接通過許多音頻的庫對wav文件進行操作,比如自帶的標準庫wave庫,還有如eyeD3,PyAudio,Audacity等等.我們先不介紹這種方式,我們先通過傳統的文件操作以二進制的形式讀取Wav文件,來分析一下它的頭文件來驗證一下我們前面有關CHUNK所學的知識.通過二進制操作音頻文件並取得前四個字節的代碼如下:(我們的測試音頻是night.wav,已放在github中,通過我的博客園右上角的綠色圖標可以鏈接到我的github界面,找到lab102下的w8目錄即可獲取該資源,或者找到本文博客園最上方資源):
#讀取wav前四個字節內容 -xlxw
file = open("night.wav", "rb")
s = file.read(4)
print(s)
程序運行截圖:
我們可以看到最前面的字節和我們認為的沒錯,是RIFF,那我們來讀取44個字節來看看其中的信息是怎麽樣的:
#讀取wav前44個字節內容 -xlxw
file = open("night.wav", "rb")
s = file.read(44)
print(s)
程序運行截圖:
我們可以看到RIFF後面的字符串為WAVE的Form Type,以及fmt,data這幾個FOURCC,而其他用十六進制表示的就是數據大小/數據了.所以我們通過二進制讀取的WAV文件的信息和我們前面學習的CHUNK中的內容是一致的.
用Wave庫提取wav文件信息
在我們前面中的介紹可以知道wav文件的存儲方式,並且能夠簡單的提取其中的信息了,而我們知道wav文件最重要的就是聲音信息的存貯,這一部分我們也可以通過對CHUNK的DATA 進行分析,不過我們在python中有更加簡單的獲得聲音的方式,那就是利用python自帶的wave庫,我們下面就來介紹一下wave庫的一些方法,為後文做鋪墊.
- 首先引入wave庫
import wave
- open()
打開一個聲音文件,使用方法wave.open(聲音文件地址,模式)
其中聲音文件地址就是wav文件位置,模式和文件讀寫差不多,如“wb”-只寫;“rb”-只讀方式 b代表以二進制模式打開.
- close()
關閉聲音文件
- getparams()
獲取wav文件的參數(以tuple形式輸出),依次為(聲道數,采樣精度,采樣率,幀數,......)
如下圖為本文例子night.wav的getparams()的信息:
- readframes()
得到每一幀的聲音數據,返回的值是二進制數據,在python中用字符串表示二進制數據,如下圖,所以我們後面要進行轉化.
得到的night.wav的前10幀的數據如下圖所示:
上面就基本上是wave庫的常用方法了.下面會對此進行應用了.
繪制WAV文件的波形
我們經常在許多聲音軟件,比如CoolEdit,Audition等軟件中能看到聲音文件的波形,所以我們在這裏利用Python以及前面介紹的wave庫,輔以numpy,Matplotlib來嘗試繪制night.wav文件的波形.
下面我們來簡單講述一下繪制波形的步驟:(以night.wav為例子)
- 通過wav庫獲得night.wav的頭文件中的信息,如采樣率/聲道數等等.
- 提取出DATA區域的信息,用numpy將string格式數據轉化為數組
- 通過判定聲道數將DATA區域數據進行處理(對數組矩陣進行轉換)
- 得到每個繪制點的時間(x坐標)
- 用matplotlib庫提供的方法繪制出波形圖
我們來對這些步驟中的一些部分詳細說一下:
對DATA區域的處理
因為night.wav是一個雙聲道的WAV 文件,我們從上文對DATA區域格式的介紹可以知道儲存形式是左聲道/右聲道的形式存貯數據,所以在這裏我們要對提取出的數據進行處理,這裏numpy庫為我們提供了很好的解決方法,我們主要用到了改變形狀的Shape方法以及T轉置方法,我們在這裏來舉一個例子:
我們創建一個數組[1,2,3,4,5,6,7,8]裏面有8個元素,這時候根據我們的分離方法,應該分為左聲道[1,3,5,7]和右[2,4,6,8],我們可以通過shape先改變矩陣的形狀使數據變為兩列分別為左右聲道,再通過轉置得到最終數據,我們的例子可以用如下圖所示理解:
將數據分為左右聲道的例子
繪制波形圖的matplotlib庫
這裏我們要繪出波形,所以用matplotlib庫大大減少我們的繪圖難度,我們主要用到plt.subplot和plt.plot這兩個方法,所以我們對這兩個方法進行解釋.
- plt.subplot()
這是用於matplotlib繪制多個子圖的方法,因為我們這裏音頻文件要分為兩個部分(左聲道/右聲道)
即分成2X1的形式,所以我們的第一個繪圖和第二個繪圖分別用
plt.subplot(211)和plt.subplot(212)來表示,如下圖所示:
matplotlib子圖
- plt.plot
plt.plot()是用於繪制線條的方法,我們用到了其中的三個參數
(X坐標,y坐標,顏色)
就能用plot畫出最終的波形圖了
- 其他的方法這裏就不介紹了,但是matplotlib的繪圖功能相當強大.
繪制night.wav波形的代碼
用於繪制出wav文件波形的代碼如下(這裏我們還是以night.wav作為例子)
#wave data -xlxw
#import
import wave as we
import numpy as np
import matplotlib.pyplot as plt
def wavread(path):
wavfile = we.open(path,"rb")
params = wavfile.getparams()
framesra,frameswav= params[2],params[3]
datawav = wavfile.readframes(frameswav)
wavfile.close()
datause = np.fromstring(datawav,dtype = np.short)
datause.shape = -1,2
datause = datause.T
time = np.arange(0, frameswav) * (1.0/framesra)
return datause,time
def main():
path = input("The Path is:")
wavdata,wavtime = wavread(path)
plt.title("Night.wav‘s Frames")
plt.subplot(211)
plt.plot(wavtime, wavdata[0],color = ‘green‘)
plt.subplot(212)
plt.plot(wavtime, wavdata[1])
plt.show()
main()
程序繪制出的波形圖的截圖:
night.wav聲音文件的雙聲道波形
總結&拓展
在本周我學到了wav文件的存貯格式以及怎麽用python讀取wav文件信息並且繪制出波形圖.其中在繪制了波形圖後,我們可以對波形多透露出的信息進行分析,比如:
- 得到聲音的特征
- 分析錄音時被錄音者是否停止說話
等等用途,可以用於許多方面,如聲音識別,斷句(音頻分割)等等許多的場景.這裏在以後我也會繼續進行探索.
【轉】Python處理wave文件