Python聲音處理入門

  • 2020 年 1 月 10 日
  • 筆記

Python聲音處理入門

註:本文加入了譯者的理解,並非嚴謹的譯作,僅供參考。


原文Basic Sound Processing with Python描述了怎樣在Python中通過pylab介面對聲音進行基本的處理。

準備工作

安裝庫

確保庫numpy、matplotlib和scipy已正確安裝。

導入包

from pylab import*  from scipy.io import wavfile

讀取wav文件

讀取wav文件 下載文件440_sine.wav,文件中加入了基頻(F0)為440Hz的雜訊。

sampFreq, snd = wavfile.read('440_sine.wav')

函數scipy.io.wavefile.read以int16或int32(32位wav)格式讀入wav文件。16位.wav文件對應int16,32位.wav文件對應int32,不支援24位.wav。

查看wav文件類型

>>>snd.dtype  dtype('int16')

這表示原始聲壓值在wav文件中一一映射到區間[-2^15, 2^15 -1]。我們把聲壓值歸一化,即映射到區間[-1, 1):

snd = snd / (2.**15)

查看wav文件的通道數和取樣點數

>>> snd.shape  (5060, 2)

表示文件包含2個通道,5060個取樣點。結合取樣率(sampFreq = 44110),可得訊號持續時長為114ms:

>>> 5060.0 / sampFreq  0.11473922902494331

下文我們只處理其中一個通道

s1 = snd[:, 0]

python自身不支援播放聲音,假如你想在python中回放聲音,參考pyalsaaudio(Linux)或PyAudio。

2 繪製音調圖

以時間(單位ms)為x軸,聲壓值為y軸,繪製音調圖。先創建時間點數組

timeArray = arange(0, 5060.0, 1)   #[0s, 1s], 5060個點  timeArray = timeArray / sampFreq   #[0s, 0.114s]  timeArray = timeArray * 1000       #[0ms, 114ms]

然後繪圖

plot(timeArray, s1, color='k')  ylabel('Amplitude')  xlabel('Time (ms)')

3 繪製頻譜圖

頻譜圖也是一種很有用的圖形表示方式。用函數fft對聲音進行快速傅立葉變換(FFT),得到聲音的頻譜。讓我們緊跟技術文檔的步伐,得到聲音文件的功率譜:

n = len(s1)  p = fft(s1)         #執行傅立葉變換

技術文檔中指定了執行fft用到的抽樣點數目,我們這裡則不指定,默認使用訊號n的取樣點數。不採用2的指數會使計算比較慢,不過我們處理的訊號持續時間之短,這點影響微不足道。

nUniquePts = ceil((n+1)/2.0)  p = p[0:nUniquePts]  p = abs[p]

fft變換的返回結果為複合形式,比如複數,包含幅度和相位資訊。我們獲取傅立葉變換的絕對值,得到頻率分量的幅度資訊。

p = p / float(n)    #除以取樣點數,去除幅度對訊號長度或取樣頻率的依賴  p = p**2            #求平方得到能量    #乘2(詳見技術手冊)  #奇nfft排除奈奎斯特點  if n % 2 > 0:       #fft點數為奇  p[1:len(p)] = p[1:len(p)]*2  else:               #fft點數為偶  p[1:len(p)-1] = p[1:len(p)-1] * 2    freqArray = arange(0, nUniquePts, 1.0) * (sampFreq / n)  plot(freqArray/1000, 10*log10(p), color='k')  xlabel('Freqency (kHz)')  ylabel('Power (dB)')

繪製的頻譜圖如下所示。注意圖中y軸是能量的對數10*log10(p),單位分貝;x軸是頻率/1000,單位kHz。

為了檢驗計算結果是否等於訊號的能量,我們計算出訊號的均方根rms。廣義來說,可以用rms衡量波形的幅度。如果直接對偏移量為零的正弦波求幅度的均值,它的正負部分相互抵消,結果為零。那我們先對幅度求平方,再開方(注意:開方加大了幅度極值的權重?)

rms_val = sqrt(mean(s1**2))  rms_val  0.0615000626299

訊號的rms等於總能量的平方根,那麼把fft在所有頻率上的能量值相加然後求平方根,應該等於rms。

>>> sqrt(sum(p))  0.0615000626299

完整程式碼

Tags: