網站首頁 編程語言 正文
1.語音信號的產生與特性
我們要對語音進行分析,首先要提取能夠表示該語音的特征參數,有了特征參數才可能利用這些參數進行有效的處理,在對語音信號處理的過程中,語音信號的質量不僅取決于處理方法,同時取決于時候選對了合適的特征參數。
語音信號是一個非平穩的時變信號,但語音信號是由聲門的激勵脈沖通過聲道形成的,而聲道(人的口腔、鼻腔)的肌肉運動是緩慢的,所以“短時間”(10~30ms)內可以認為語音信號是平穩時不變的。由此構成了語音信號的“短時分析技術”。
提取的不同的語音特征參數對應著不同的語音信號分析方法:時域分析、頻域分析、倒譜域分析…由于語音信號最重要的感知特性反映在功率譜上,而相位變化只起到很小的作用,所有語音頻域分析更加重要。
2.語音的讀取
本實驗使用wave庫,實現語音文件的讀取、波形圖繪制,相關的庫還有librosa、scipy等
import wave #調用wave模塊
import matplotlib.pyplot as plt #調用matplotlib.pyplot模塊作為Plt
import numpy as np #調用numpy模塊記作np
import scipy.signal as signal
import pyaudio
%matplotlib inline
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來正常顯示中文標簽
plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示符號
f = wave.open(r"C:\Users\zyf\Desktop\Jupyter\1.wav", "rb")#讀取語音文件
params = f.getparams() #返回音頻參數
nchannels, sampwidth, framerate, nframes = params[:4] #賦值聲道數,量化位數,采樣頻率,采樣點數
print(nchannels,sampwidth,framerate,nframes)# 輸出聲道數,量化位數,采樣頻率,采樣點數
str_data = f.readframes(nframes) # 讀取nframes個數據,返回字符串格式
f.close()
wave_data = np.frombuffer(str_data, dtype=np.short) # 將字符串轉換為數組,得到一維的short類型的數組
wave_data = wave_data * 1.0 / (max(abs(wave_data))) # 賦值的歸一化
time = np.arange(0, nframes) * (1.0 / framerate) # 最后通過采樣點數和取樣頻率計算出每個取樣的時間
# 整合左聲道和右聲道的數據,如果語音為雙通道語音,具體代碼需做調整
#wave_data = np.reshape(wave_data, [nframes, nchannels])
# wave_data.shape = (-1, 2) # -1的意思就是沒有指定,根據另一個維度的數量進行分割
plt.figure() # 單通道語音波形圖
plt.plot(time, wave_data[:])
plt.xlabel("時間/s",fontsize=14)
plt.ylabel("幅度",fontsize=14)
plt.title("波形圖",fontsize=14)
plt.grid() # 標尺
plt.tight_layout() # 緊密布局
plt.show()
3.語音的播放
# 音頻的播放,本實驗使用pyaudio(代碼相對matlab較麻煩,后期簡化)
import pyaudio
import wave
chunk = 1024
wf = wave.open(r"C:\Users\zyf\Desktop\Jupyter\1.wav", 'rb')
p = pyaudio.PyAudio()
# 打開聲音輸出流
stream = p.open(format = p.get_format_from_width(wf.getsampwidth()),
channels = wf.getnchannels(),
rate = wf.getframerate(),
output = True)
# 寫聲音輸出流到聲卡進行播放
while True:
data = wf.readframes(chunk)
if data == "":
break
stream.write(data)
stream.stop_stream()
stream.close()
p.terminate() # 關閉PyAudio
4.音頻文件的寫入
# 音頻文件的寫入、存儲
# 使用wave庫,相關的庫還有librosa、scipy等,讀寫操作上的差異參閱博客: https://blog.csdn.net/weixin_38346042/article/details/119906391
import wave
import numpy as np
import scipy.signal as signal
framerate = 44100 # 采樣頻率
time = 10 # 持續時間
t = np.arange(0, time, 1.0/framerate)
# 調用scipy.signal庫中的chrip函數,
# 產生長度為10秒、取樣頻率為44.1kHz、100Hz到1kHz的頻率掃描波
wave_data = signal.chirp(t, 100, time, 1000, method='linear') * 10000
# 由于chrip函數返回的數組為float64型,
# 需要調用數組的astype方法將其轉換為short型。
wave_data = wave_data.astype(np.short)
# 打開WAV音頻用來寫操作
f = wave.open(r"sweep.wav", "wb")
f.setnchannels(1) # 配置聲道數
f.setsampwidth(2) # 配置量化位數
f.setframerate(framerate) # 配置取樣頻率
comptype = "NONE"
compname = "not compressed"
# 也可以用setparams一次性配置所有參數
# outwave.setparams((1, 2, framerate, nframes,comptype, compname))
# 將wav_data轉換為二進制數據寫入文件
f.writeframes(wave_data.tobytes())
f.close()
5.語音的分幀加窗
5.1 分幀
語音數據和視頻數據不同,本沒有幀的概念,但是為了傳輸與存儲,我們采集的音頻數據都是一段一段的。為了程序能夠進行批量處理,會根據指定的長度(時間段或者采樣數)進行分段,結構化為我們編程的數據結構,這就是分幀。語音信號在宏觀上是不平穩的,在微觀上是平穩的,具有短時平穩性(10—30ms內可以認為語音信號近似不變),這個就可以把語音信號分為一些短段來進行處理,每一個短段稱為一幀(CHUNK)。
5.2 幀移
由于我們常用的信號處理方法都要求信號是連續的,也就說必須是信號開始到結束,中間不能有斷開。然而我們進行采樣或者分幀后數據都斷開了,所以要在幀與幀之間保留重疊部分數據,以滿足連續的要求,這部分重疊數據就是幀移。
幀長=重疊+幀移
5.3 加窗
我們處理信號的方法都要求信號是連續條件,但是分幀處理的時候中間斷開了,為了滿足條件我們就將分好的幀數據乘一段同長度的數據,這段數據就是窗函數整個周期內的數據,從最小變化到最大,然后最小。
常用的窗函數:矩形窗、漢明窗、海寧窗
加窗即與一個窗函數相乘,加窗之后是為了進行傅里葉展開.
1.使全局更加連續,避免出現吉布斯效應
2.加窗時候,原本沒有周期性的語音信號呈現出周期函數的部分特征。
加窗的代價是一幀信號的兩端部分被削弱了,所以在分幀的時候,幀與幀之間需要有重疊。
# 加窗分幀(接上)
# 語音分幀、加窗
wlen=512 # 每幀信號長度
inc=128 # 幀移
signal_length=len(wave_data) #信號總長度
print(signal_length)
if signal_length<=wlen: #若信號長度小于一個幀的長度,則幀數定義為1
nf=1
else: #否則,計算幀的總長度
nf=int(np.ceil((1.0*signal_length-wlen+inc)/inc)) # nf 為幀數
# np.ceil向上取整,所以會導致實際分幀后的長度大于信號本身的長度,所以要對原來的信號進行補零
pad_length=int((nf-1)*inc+wlen) #所有幀加起來總的鋪平后的長度
zeros=np.zeros((pad_length-signal_length,)) #不夠的長度使用0填補,類似于FFT中的擴充數組操作
pad_signal=np.concatenate((wave_data,zeros)) #填補后的信號記為pad_signal
indices=np.tile(np.arange(0,wlen),(nf,1))+np.tile(np.arange(0,nf*inc,inc),(wlen,1)).T #相當于對所有幀的時間點進行抽取,得到nf*wlen長度的矩陣
indices=np.array(indices,dtype=np.int32) #將indices轉化為矩陣
frames=pad_signal[indices] #得到幀信號,587*512的矩陣信號
#a=frames[30:31]
#print(frames.shape)
winfunc = signal.hamming(wlen) # 調用窗函數,本初以漢明窗為例
#print(winfunc.shape)
win=np.tile(winfunc,(nf,1)) #窗函數為一維數組(512,),因此需要按照信號幀數進行變換得到(587*512)矩陣信號
#print(win.shape)
my = frames*win # 這里的*指的是計算矩陣的數量積(即對位相乘)。
# python中矩陣運算分為兩種形式,一是np.array,而是np.matrix
# ----------------------------------
# 繪制分幀加窗后的圖像(接上)
# 因為分幀加窗后的信號為587*512的矩陣信號,為了繪圖,將其轉換為一維信號
t=my.flatten()
t=t.T
print(t.shape)
time = np.arange(0, len(t)) * (1.0 / framerate) # 調整時間軸
plt.figure()
plt.plot(time,t,c="g")
plt.grid()
plt.show()
6.語音的頻譜分析
6.1 頻譜圖
通過FFT對時域語音信號進行處理,得到頻譜圖
import numpy as np
from scipy.io import wavfile
import matplotlib.pyplot as plt
%matplotlib inline
sampling_freq, audio = wavfile.read(r"C:\Users\zyf\Desktop\Jupyter\1.wav") # 讀取文件
audio = audio / np.max(audio) # 歸一化,標準化
# 應用傅里葉變換
fft_signal = np.fft.fft(audio)
print(fft_signal)
fft_signal = abs(fft_signal)
print(fft_signal)
# 建立時間軸
Freq = np.arange(0, len(fft_signal))
# 繪制語音信號的
plt.figure()
plt.plot(Freq, fft_signal, color='blue')
plt.xlabel('Freq (in kHz)')
plt.ylabel('Amplitude')
plt.show()
6.2 語譜圖
語譜圖綜合了時域和頻域的特點,明顯的顯示出來了語音頻率隨時間的變化情況**,語譜圖的橫軸為時間,縱軸為頻率任意給定頻率成分在給定時刻的強弱用顏色深淺表示。**顏色深表示頻譜值大,顏色淺表示頻譜值小,譜圖上不同的黑白程度形成不同的紋路,稱為聲紋,不用講話者的聲紋是不一樣的,可以用做聲紋識別。
import wave
import matplotlib.pyplot as plt
import numpy as np
f = wave.open(r"C:\Users\zyf\Desktop\Jupyter\1.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
strData = f.readframes(nframes)#讀取音頻,字符串格式
waveData = np.fromstring(strData,dtype=np.int16)#將字符串轉化為int
waveData = waveData*1.0/(max(abs(waveData)))#wave幅值歸一化
waveData = np.reshape(waveData,[nframes,nchannels]).T
f.close()
plt.specgram(waveData[0],Fs = framerate, scale_by_freq = True, sides = 'default')
plt.ylabel('Frequency(Hz)')
plt.xlabel('Time(s)')
plt.colorbar()
plt.show()
參考博客:
- https://www.cnblogs.com/zhenmeili/p/14830176.html
- https://blog.csdn.net/sinat_18131557/article/details/105340416
- https://blog.csdn.net/weixin_38346042/article/details/119906391
- https://www.jb51.net/article/126984.htm
總結?
原文鏈接:https://blog.csdn.net/fengqiyunfeiyangya/article/details/124564232
相關推薦
- 2023-02-15 Python函數常見幾種return返回值類型_python
- 2022-10-12 Android自定義View實現遙控器按鈕_Android
- 2023-07-26 TypeScript中的泛型(泛型函數、接口、類、泛型約束)
- 2023-07-02 jQuery和HTML對某個標簽設置只讀或者禁用屬性的方式_jquery
- 2022-09-23 C#實現目錄跳轉(TreeView和SplitContainer)的示例代碼_C#教程
- 2022-12-10 c++如何保存vector到文件_C 語言
- 2022-10-14 scikit-learn工具包中分類模型predict_proba、predict、decision
- 2023-12-08 Can‘t open the append-only file: Permission denied
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支