- python打開音頻文件(IO)
- 語音音量大小與響度的相關計算
語音處理最基礎的部分就是如何對音頻文件進行處理。
聲音的物理意義:聲音是一種縱波,縱波是質點的振動方向與傳播方向同軸的波。如敲鑼時,鑼的振動方向與波的傳播方向就是一致的,所以聲波是縱波。縱波是波動的一種(波動分為橫波和縱波)
通常情況下對聲音進行采樣量化之后得到了聲音的“時間—振幅”信息。
Python 打開wav文件的操作
wav文件
利用python打開一個wav音頻文件,然后分析wav文件的數據存儲格式,有了格式之后就能很方便的進行一些信號處理的操作。Wikipedia給出的wav文件的資料如下
Waveform Audio File Format (WAVE, or more commonly known as WAV due to its filename extension - both pronounced "wave"‘)(rarely, Audio for Windows) is a Microsoft and IBM audio file format standard for storing an audio bitstream on PCs. It is an application of the Resource Interchange File Format (RIFF) bitstream format method for storing data in "chunks", and thus is also close to the 8SVX and the AIFF format used on Amiga and Macintosh computers, respectively. It is the main format used on Windows systems for raw and typically uncompressed audio. The usual bitstream encoding is the linear pulse-code modulation (LPCM) format.
音頻格式資料在網上也有一些標准格式的說明,比如WAVE PCM soundfile format http://soundfile.sapp.org/doc/WaveFormat/
關於Python中對wave文件的處理,可以參考官方給出的文檔 https://docs.python.org/3/library/wave.html
當然大多數情況下,很需要對信息進行篩選的能力,在使用python來處理wav文件的時候僅僅需要其中的幾個操作,並不一定要每個都掌握,在需要的時候查詢文檔。
注意
大段的文字來敘述wav音頻文件是什么並不是作為編程練習的目的,人的精力是有限的不可能同時掌握所有的知識點。一開始,就學習聲音的原理,然后再驗證,這些工作應該是由搞信息編碼的研究者來完成,而作為一個工程師應該集中精力研究代碼上的復現。不如反其道行之,讀取音頻文件,根據系統提供的API獲得各種參數,再去查詢參數的信息。編程練習要思考怎么用一般在處理音頻文件的時候。有個流程就是將音頻文件導入,分析波形,進行傅里葉變換之類的操作,作為數據的預處理,才可以進行下一步數值數據的處理,其實音頻處理最想要的過程無非如上所述。所以正常情況下,還要知道音頻文件中的數據的含義,用計算機科學的思想分析就是需要知道它的數據結構。
音頻文件信息
用到了python 處理wav文件的包 wave,讀取wave 信息,遍歷參數
import wave as we filename = 'child1.wav' WAVE = we.open(filename) for item in enumerate(WAVE.getparams()): print(item)
查詢官方文檔中對於wave.getparams()的描述
Wave_read.
getparams
()Returns a
namedtuple()
(nchannels, sampwidth, framerate, nframes, comptype, compname)
, equivalent to output of theget*()
methods.
輸出信息(聲道,采樣寬度,幀速率,幀數,唯一標識,無損)
采樣點的個數為 2510762,采樣的頻率為44100HZ,通過這兩個參數可以得到聲音信號的時長
每個采樣點是16 bit = 2 bytes ,那么將采樣點的個數 2510762*2/(1024*1024)=4.78889MB,那么這個信息就是文件大小信息。
檢驗一下聲音波形的時間
child1.wav 4.78MB,時長56s
time = 56.93337868480726
根據上面WAVE PCM soundfile format 的資料信息查詢。有一個印象:WAV文件中由以下三個部分組成:
1."RIFF" chunk descriptor 2.The "fmt" sub-chunk 3.The "data" sub-chunk 存這些信息的時候都要要有 “ID”、“大小”、“格式”,這些信息標注了數據的位置,“WAV”格式由“fmt”和“data”,兩個部分組成,其中“fmt”的存儲塊用來存音頻文件的格式,“data”的存儲塊用來存實際聽到的聲音的信息,物理上描述的振幅和時間:長度(時間)和振幅,當然人的耳朵聽聽見的是長度和音調。也就是說可以讀取這個數組,在配合頻率的信息直接畫出波形圖。
注意一下幾點
1.一個采樣點的值代表了給定時間內的音頻信號,一個采樣幀由適當數量的采樣點組成並能構成音頻信號的多個通道。
2.對於立體聲信號一個采樣幀有兩個采樣點,一個采樣點對應一個聲道。一個采樣幀作為單一的單元傳送到數/模轉換器(DAC),以確保正確的信號能同時發送到各自的通道中。
3.單聲道振幅數據為n*1矩陣點,立體聲為n*2矩陣點,那么將來文件的信息處理通過一個矩陣來實現
WAV文件波形
為了繪制波形圖,需要的參數有時間和振幅的信息,完整的步驟如下:
1.將WAV文件導入到Python的工作環境中。
2.設置參數,聲音信號(時間、振幅、頻率)。
3.將這些信息通過 matplotlib.pyplot提供的接口繪畫出來。
可以得到一個振幅隨着時間變化的函數。
1 import wave as we 2 import matplotlib.pyplot as plt 3 import numpy as np 4 from scipy.io import wavfile 5 6 filename = 'child1.wav' 7 WAVE = we.open(filename) 8 print('---------聲音信息------------') 9 for item in enumerate(WAVE.getparams()): 10 print(item) 11 a = WAVE.getparams().nframes # 幀總數 12 f = WAVE.getparams().framerate # 采樣頻率 13 sample_time = 1/f # 采樣點的時間間隔 14 time = a/f #聲音信號的長度 15 sample_frequency, audio_sequence = wavfile.read(filename) 16 print(audio_sequence) #聲音信號每一幀的“大小” 17 x_seq = np.arange(0,time,sample_time) 18 19 plt.plot(x_seq,audio_sequence,'blue') 20 plt.xlabel("time (s)") 21 plt.show()
WAV文件的存儲格式
通過上述操作流程,了解了幾個信息:
1.通過python現有包wave 可以獲取wav文件幾個基礎信息,比如文件的聲道,聲音的采樣寬度,幀速率,幀數,是否唯一標識,是否無損,關於WAV文件處理的入門信息,可先用幾行代碼獲得WAV文件基本處理信息,然后將這些信息通過畫圖的形式繪畫出來。
2.WAV文件中的時間,可以按照采樣點數目,以及頻率的大小獲得,將來可以加窗口,進行數字信號處理上的一些操作,對聲音信號作進一步的分析。
3.WAV文件中將聲音信號存為n*1矩陣點或者n*2矩陣點,區分條件為是單聲道還是雙聲道,對聲音信號的研究可以轉化為這個數組的研究。
聲音信號的意義
常見的研究問題中,如果我們想要知道語音音量大小或者頻率,即經常提到聲音的物理意義:有聲音的大小以及聲音的頻率。
a) 聲音的響度
b) 聲音的頻率
關於語音信號,根據經驗知道,只有聲源發出聲音超過一定的時間,或者聲音夠大才能被人耳感知,這個時候就需要類似一個采樣窗口的概念。通過采樣窗口來描述時間刻度,注意,前文提到了音頻文件的采樣率為$F_s$,表示麥克風每隔$1/F_s$秒,采樣一個電平,如果$F_s=44100Hz$,那么表示時間間隔為$ 2.25\times 10^{-5} s$,播放N個采樣點的時長為$N \times 1/F_s$,根據經驗,只有這個時長超過20~40ms才能被人耳感知,也就是說需要有一定的時間上的“積分效應”。
聲音的響度和能量有關,那么就可以根據上述描述,在根據精度在一定時長內采樣,獲得那個時刻能量。有關精度的計算和采樣窗口有關,如果采樣窗口為4096,窗口重疊為2048,那么采樣后那個時刻的時長為$4096\times 1/F_s = 92.8ms$,時間步長(刻度或者稱為精度)為 $2048 \times 1/F_s = 46.4ms $,此時聲音的能量可以通過采樣窗口內的振幅值的平方和描述那個時刻的能量大小:$\sum_{i=0}^{N}x^2(i)$,在經過Log的轉換就是我們常見的,以DB為單位的刻度。
聲音的頻率即聲音的周期性,只有一定時長的語音信號才具有周期性,當然在很多和音樂相關的文獻中也說明了基音和泛音,大多數時候,聲音的頻率都是指的是基音,那么求基音的算法有很多。基音的原理主要是認為聲音具有周期性,此時假設聲音信號為$x(t)$,那么在加窗口采樣后的那個時刻內,假設頻率為$F$,那么周期為$T=1/F$,有$x(t+T)=x(t)$,此時只需要求出T即可,大多數算法都是基於以下的原理求出:
1) 信號每隔T個時刻都有一個波峰,如果考慮信號的延遲,在窗長為n的窗口內,內每隔$\tau, \tau=0,1,2,3,....,n$個延遲的采樣信號為$x_{\tau}(t)$,當然$\tau$ 也不一定需要到n,$x_{\tau}(t) \times x(t)$,此時求出的波形對應的第一個波峰出現的位置,即$\tau$的取值就是基音對應的周期點的個數,周期為$T/Fs $s,此時的倒數就是頻率。
2) 當然也有類似反過來的原理,$x(t+T)=x(t)$=>$x(t+T)-x(t)=0$,通過絕對值或者平方和,找到第一個零點的位置求解。
頻率和幀能量求解例子
1)加窗口采樣、得到幀級別的聲音信號
import numpy as np from scipy.signal import windows # 分幀處理函數 def generate_frame_data(x, sr, win, overlap): n_frames = 1 + int(len(x) / overlap) y = np.zeros((win, n_frames)) x = np.pad(x, (0, n_frames * overlap + win - len(x)), 'constant', constant_values=0) for n in range(n_frames): y[:, n] = x[n * overlap:n * overlap + win] time = np.arange(0, len(x)/sr,overlap/sr) return y,time
2)短時幀能量和自相關函數
#自相關函數 def compute_acf(wave_data): auto_corr_funtion = [] for item in wave_data: auto_corr_funtion.append(compute_frame_acf(item)) return np.array(auto_corr_funtion) # 計算幀能量 def compute_frame_ste(frame_data): short_time_energy = np.sum(frame_data * frame_data, axis=0) return short_time_energy[:-1]
3)計算頻率
# 計算分幀之后的結果最大值 def comput_T0_acf(auto_corr_function): T0 = np.argmax(auto_corr_function[:, 10:], axis=1) return T0