end2end-asr-pytorch - audio processing - speech signal processing
https://github.com/gentaiscool/end2end-asr-pytorch
採樣頻率/取樣頻率是每秒鐘採集聲音樣本的次數。採樣頻率越高,聲音質量越好,聲音還原越真實,同時佔用資源越多。
採樣位數/量化精度/採樣值/取樣值是採樣樣本幅度量化,用來衡量聲音波動變化的一個參數。數值越大,分辨率越高,發出聲音的能力越強。
採樣數據記錄的是振幅,採樣精度取決於採樣位數的大小:
1 字節 (8 bit) 記錄 256 = 2^8
個等級,振幅劃分成 256 個等級。
2 字節 (16 bit) 記錄 65536 = 2^16
個等級 。
4 字節 (32 bit) 振幅細分到 4294967296 = 2^32
個等級。
音頻單通道和雙通道是聲音通道的數目。單聲道的聲音只能使用一個喇叭發聲,立體聲可以使兩個喇叭都發聲 (左右聲道),更能感受到空間效果。
音頻幀記錄了一個聲音單元,單幀長度爲採樣位數和通道數的乘積。
交錯模式數據以連續幀的方式存放,首先記錄幀 1 的左聲道樣本和右聲道樣本,依次記錄幀 2、3、4、… 的左聲道樣本和右聲道樣本。
非交錯模式首先記錄的是一個週期內所有幀的左聲道樣本,再記錄所有右聲道樣本。
比特率是每秒的傳輸速率 bps (位速或比特率)。
1. normalization (歸一化) + multiple channels average (多通道均值)
end2end-asr-pytorch/utils/audio.py
https://github.com/gentaiscool/end2end-asr-pytorch/blob/master/utils/audio.py
def load_audio(path):
sound, _ = torchaudio.load(path, normalization=True)
sound = sound.numpy().T
if len(sound.shape) > 1:
if sound.shape[1] == 1:
sound = sound.squeeze()
else:
sound = sound.mean(axis=1) # multiple channels, average
return sound
load(filepath, out=None, normalization=True, channels_first=True, num_frames=0, offset=0, signalinfo=None, encodinginfo=None, filetype=None)
Loads an audio file from disk into a tensor. - 將音頻文件從磁盤加載到張量中。
1.1 Args
filepath (str or pathlib.Path)
: Path to audio file. - 音頻文件的路徑。
out (torch.Tensor, optional)
: An output tensor to use instead of creating one. (Default: None
).
normalization (bool, number, or callable, optional)
: If boolean True
, then output is divided by 1 << 31
= 2147483648
= 2^31
(assumes signed 32-bit audio), and normalizes to [-1, 1]
.
If number
, then output is divided by that number. - 輸出除以該數字。
If callable
, then the output is passed as a parameter to the given function, then the output is divided by the result. (Default: True
)
channels_first (bool)
: Set channels first or length first in result. (Default: True
) - channels first - [C x L]
.
num_frames (int, optional)
: Number of frames to load. num_frames = 0
to load everything after the offset. (Default: 0
)
offset (int, optional)
: Number of frames from the start of the file to begin data loading. (Default: 0
)
signalinfo (sox_signalinfo_t, optional)
: A sox_signalinfo_t type, which could be helpful if the audio type cannot be automatically determined. (Default: None
)
encodinginfo (sox_encodinginfo_t, optional)
: A sox_encodinginfo_t type, which could be set if the audio type cannot be automatically determined. (Default: None
)
filetype (str, optional)
: A filetype or extension to be set if sox cannot determine it automatically. (Default: None
)
1.2 Returns
Tuple[torch.Tensor, int]
: An output tensor of size [C x L]
or [L x C]
(Default: channels first) where L
is the number of audio frames and C
is the number of channels. An integer which is the sample rate of the audio (as listed in the metadata of the file).
如果所要提取的語音特徵不區分聲道,則必須將多聲道的語音轉換成單聲道。轉換成單聲道語音,需要計算多聲道語音數據平均值即可。單聲道的語音,不需要做轉換。
2. pre-emphasis (預加重)
預加重的目的是隻保留一定頻率範圍的信號,這個過程起到了高通濾波器的作用。高通濾波器對高頻信號有着很好的放大作用,而且會大幅度壓縮低頻信號的幅度。同時,還會產生一個相位滯後的效應,這個對高頻信號尤爲明顯。這個過程會在一定程度上消除脣齒效應。
語音處理通常使用 0.9 - 0.97,pre-emphasis = 0.97 比較常用。
3. 重採樣
語音信號可能來自不同的設備,它們在錄製的時候所設置的參數也不盡相同,最重要的一個就是採樣率。
4. 語音信號分幀
hop_length - 幀移
win_length - 窗長
語音分析使用短時分析技術。語音信號是隨時間變化的,它是一個非平穩態過程,不能用處理平穩信號的數字信號處理技術對其進行分析處理。由於不同的語音是由人的口腔肌肉運動構成聲道某種形狀而產生的響應,而這種口腔肌肉運動相對於語音頻率來說是非常慢的。雖然語音信號具有時變特性,但是在一個短時間範圍內 (10ms - 30ms),其特性基本保持不變,相對穩定,因而可以看作是一個準穩態過程,即語音信號具有短時平穩性。
語音信號處理常常要弄清楚語音中各個頻率成分的分佈。傅里葉變換要求輸入信號是平穩的,輸入不平穩的信號,得到的結果沒有什麼意義。語音在宏觀上來看是不平穩的,但是從微觀上來看,在比較短的時間內可以看成平穩的,就可以截取出來做傅里葉變換。
從整體上看,這段語音信號不平穩。紅框框出來的部分是一幀,在這一幀內部的信號可以看成平穩的。
宏觀上,幀長必須足夠短來保證幀內信號是平穩的。口型的變化是導致信號不平穩的原因,所以在一幀的期間內口型不能有明顯變化,一幀的長度應當小於一個音素的長度。正常語速下,音素的持續時間大約是 50~200 ms,幀長一般小於 50 ms。
微觀上,幀長必須包括足夠多的振動週期。傅里葉變換要分析頻率,只有重複足夠多次才能分析頻率。語音的基頻,男聲在 100 Hz 左右,女聲在 200 Hz 左右,換算成周期就是 10 ms 和 5 ms。一幀要包含多個週期,一般取至少 20 ms。
語音信號的分析和處理必須建立在短時的基礎上,進行短時分析,將語音信號分爲一段一段來分析其特徵參數,其中每一段稱爲一幀,幀長一般取爲 10ms - 30ms。對於整體的語音信號來講,分析出的是由每一幀特徵參數組成的特徵參數時間序列。
爲了使幀與幀之間平滑過渡,保持其連續性,分幀一般採用交疊分段的方法。前一幀和後一幀的交疊部分稱爲幀移,幀移與幀長的比值一般爲 1/2。
語音數據不同於視頻數據,語音數據原本沒有幀的概念,爲了傳輸與存儲,採集的音頻數據都是一段一段的。爲了程序能夠進行批量處理,會根據指定的長度 (時間段或者採樣數) 進行分段,結構化爲編程的數據結構,就是分幀。
由於常用的信號處理方法都要求信號是連續的,信號開始到結束,中間不能有斷開。進行採樣或者分幀後數據都斷開了,所以要在幀與幀之間保留重疊部分數據,以滿足連續的要求,重疊部分數據就是幀移。在分幀的時候,不要背靠背地截取,而是相互重疊一部分。相鄰兩幀的起始位置的時間差叫做幀移 (stride)。
語音信號是一個隨時間變化的隨機序列,從全局來看它並不是一個平穩隨機過程。在較短的時間內,可以認爲它是一個近似平穩的隨機過程。可以按照幀長,把一個離散序列進行分組,每一組就是一幀。爲了保證語音信號的連續性,一般相鄰兩幀之間還要有一定的重疊,重疊部分一般佔幀長的 1/3 - 1/2。
每幀長度爲 wlen
,後一幀對前一幀的位移量爲 inc
,則重疊部分overlap = wlen - inc
。
5. time-domain windowing (時域加窗)
每一幀信號,在做傅里葉變換之前,要先進行加窗操作。加窗就是一幀信號與一個窗函數相乘,加窗是爲了進行傅里葉展開。加窗的目的是讓一幀信號的幅度在兩端漸變到 0,漸變對傅里葉變換有好處,可以提高變換結果 (頻譜) 的分辨率
加窗使全局更加連續,避免出現吉布斯現象/吉布斯效應 (Gibbs phenomenon)。加窗時,原本沒有周期性的語音信號呈現出週期函數的部分特徵。
加窗的代價是一幀信號兩端的部分被削弱了,沒有像中央的部分那樣得到重視。彌補的辦法是,幀不要背靠背地截取,要相互重疊一部分。相鄰兩幀的起始位置的時間差叫做幀移,常見的取法是取爲幀長的一半。
將語音信號分幀後,需要對每一幀信號進行分析處理。對每一幀,選擇一個窗函數,窗函數的寬度就是幀長。加窗就相當於把每一幀裏面對應的元素變成它與窗序列對應元素的乘積。窗函數一般具有低通特性,加窗函數的目的是減少頻域中的泄漏。
5.
對一幀信號做傅里葉變換,得到的結果叫頻譜,就是下圖中的藍線。
上圖中的橫軸是頻率,縱軸是幅度。頻譜上就能看出這幀語音在 480 Hz 和 580 Hz 附近的能量比較強。語音的頻譜,常常呈現出精細結構和包絡兩種模式。
精細結構就是藍線上的一個個小峯,它們在橫軸上的間距就是基頻,它體現了語音的音高 - 峯越稀疏,基頻越高,音高也越高。
包絡是連接這些小峯峯頂的平滑曲線 (紅線),它代表了口型,發的是哪個音。包絡上的峯叫共振峯,圖中能看出四個,在 500 Hz、1700 Hz、2450 Hz、3800 Hz 附近。對每一幀信號都做這樣的傅里葉變換,就可以知道音高和口型隨時間的變化情況,用於識別出一句話說的是什麼。
在語音學中,一般只把聲帶叫聲源,口腔和鼻腔都是聲道。聲帶的振動模式是比較固定的,語音的包絡主要由聲道決定,共振峯反映聲道的形狀。