標籤:數組 tps 電腦 其他 mst sof cloud ali ibm
#本文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),資源分頁檔格式,是一種按照標記區Block Storage資料(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前四個位元組內容 -xlxwfile = open("night.wav", "rb")s = file.read(4)print(s)
程式運行:
我們可以看到最前面的位元組和我們認為的沒錯,是RIFF,那我們來讀取44個位元組來看看其中的資訊是怎麼樣的:
#讀取wav前44個位元組內容 -xlxwfile = 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庫的一些方法,為後文做鋪墊.
import wave
開啟一個音效檔,使用方法wave.open(音效檔地址,模式)
其中音效檔地址就是wav檔案位置,模式和檔案讀寫差不多,如“wb”-唯寫;“rb”-唯讀方式 b代表以二進位模式開啟.
關閉音效檔
擷取wav檔案的參數(以tuple形式輸出),依次為(聲道數,採樣精度,採樣率,幀數,......)
如為本文例子night.wav的getparams()的資訊:
得到每一幀的聲音資料,返回的值是位元據,在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這兩個方法,所以我們對這兩個方法進行解釋.
這是用於matplotlib繪製多個子圖的方法,因為我們這裡音頻檔案要分為兩個部分(左聲道/右聲道)
即分成2X1的形式,所以我們的第一個繪圖和第二個繪圖分別用
plt.subplot(211)和plt.subplot(212)來表示,如所示:
matplotlib子圖
plt.plot()是用於繪製線條的方法,我們用到了其中的三個參數
(X座標,y座標,顏色)
就能用plot畫出最終的波形圖了
- 其他的方法這裡就不介紹了,但是matplotlib的繪圖功能相當強大.
繪製night.wav波形的代碼
用於繪製出wav檔案波形的代碼如下(這裡我們還是以night.wav作為例子)
#wave data -xlxw#importimport wave as weimport numpy as npimport matplotlib.pyplot as pltdef 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,timedef 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檔案