python數據建模分析 - 語音識別

語音識別:

Getting Started!首先,我們要知道語音的產生過程

voice.png

狀態:由肺產生向外的氣流,完全放松時聲帶張開,就是平時的呼吸。如果聲帶一張一合(振動)形成周期性的脈沖氣流。這個脈沖氣流的周期稱之為——基音周期(題主所言因音色不同導致的頻率不同,事實上音色的大多是泛頻上的差異,建立在基頻之上,這個基頻就是基音周期了,泛頻可以忽略)。當然啦,這只是在發濁音(b,d,v...)時才會有,當發出清音(p,t,f...)時聲帶不振動,但是會處于緊繃狀態,當氣流涌出時會在聲帶產生湍流。清音和濁音是音素的兩大類。接下來脈沖氣流/湍流到達聲道,由聲道對氣流進行調制,形成不同的音素。多個音素組成一個音節(就漢語而言是[聲母]+韻母)。如果沒學過信號系統那就想像一下平舌音和翹舌音,z和zh發聲時肺和喉的狀態都一樣,只是舌頭動作不一樣,發出的聲音也就不一樣了,這就算是簡單的調制。從而聲音的波形會發生一些變化。這個波形,就是以后分析所需要的數據。

標識聲音的圖像有以下三種

  • 頻譜圖
  • 時譜圖
  • 語譜圖

以千里碼 語音識別-1為例,將Mp3文件轉換成wav,分析其頻譜圖
參照wav使用手冊,讓我們介紹一下wav文件

WAV是Microsoft開發的一種聲音文件格式,雖然它支持多種壓縮格式,不過它通常被用來保存未壓縮的聲音數據(PCM脈沖編碼調制)。WAV有三個重要的參數:聲道數、取樣頻率和量化位數。

  • 聲道數:可以是單聲道或者是雙聲道
  • 采樣頻率:一秒內對聲音信號的采集次數,常用的有8kHz, 16kHz, 32kHz, 48kHz, 11.025kHz, 22.05kHz, 44.1kHz
  • 量化位數:用多少bit表達一次采樣所采集的數據,通常有8bit、16bit、24bit和32bit等幾種

1.讀入二進制音頻流數據流程

  • 對于一個音頻實例wf而言,通過調用它的方法讀取WAV文件的格式和數據:getnchannels, getsampwidth, getframerate, getnframes等方法可以單獨返回WAV文件的特定的信息。
  • readframes:讀取聲音數據,傳遞一個參數指定需要讀取的長度(以取樣點為單位),readframes返回的是二進制數據

PS:注意需要使用"rb"(二進制模式)打開文件

import wave
import pyaudio
import numpy
from matplotlib import pylab


#打開wav文檔,文件路徑根據需要修改
wf = wave.open("F:\\work\\war.wav","rb")

#創建PyAudio對象
p = pyaudio.PyAudio()


class Audio(object):

    def __init__(self):
        self.channels = wf.getnchannels()
        # 返回音頻通道數
        self.rate = wf.getframerate()
        # 返回采樣頻率。
        self.format = p.get_format_from_width(wf.getsampwidth())
        # 返回指定寬度的PortAudio格式常量。
        self.stream = p.open(format=self.format,channels=self.channels,rate=self.rate,output=True)
        # 使用所需的音頻參數在所需設備上打開一個流
        self.nframes = wf.getnframes()
        # 返回音頻幀數
        self.collect_point_num = 44100
        # 采樣點數,修改采樣點數和起始位置進行不同位置和長度的音頻波形分析
        self.start = 0
        # 開始采樣位置
    def read_data(self):
        self.str_data = wf.readframes(self.nframes)
        # 讀取聲音數據,傳遞一個參數指定需要讀取的長度(以取樣點為單位),readframes返回的是二進制數據(即bytes數組)
        # print(self.str_data)
        wf.close()
        #關閉媒體流
if __name__ == '__main__':
    aduio = Audio()
    aduio.read_data()

python output

aduio.jpg

2.生成的流媒體字節數組計算出每個取樣的時間

  • 將讀取的二進制數據轉換為一個可以計算的數組
  • 通過fromstring函數將字符串轉換為數組,通過其參數dtype指定轉換后的數據格式,由于我們的聲音格式是以兩個字節表示一個取樣值,因此采用short數據類型轉換。現在我們得到的wave_data是一個一維的short類型的數組,但是因為我們的聲音文件是雙聲道的,因此它由左右兩個聲道的取樣交替構成:LRLRLRLR....LR(L表示左聲道的取樣值,R表示右聲道取樣值)'
  • 幀率的計算公式:
    采樣率 = 每秒中的采樣頻率/每秒中的采樣點數 幀率(fps) =1 /采樣率
  • 將波形數據轉換為數組
  • 通過取樣點數和取樣頻率計算出每個取樣的時間
    def convrt_data(self):
        self.df = self.rate / (self.collect_point_num - 1)
        # 根據總平均法使用全局幀數除以全局時間,以求出幀率
    def Data_collection(self):
        wave_data = numpy.fromstring(self.str_data,dtype=numpy.short)
        # 根據聲道數和量化單位,將讀取的二進制數據轉換為一個可以計算的數組

        wave_data.shape = -1, 2
        # -1代表左聲道,2代表右聲道
        # 通過fromstring函數將字符串轉換為數組,通過其參數dtype指定轉換后的數據格式,由于我們的聲音格式是以兩個字節表示一個取樣值,
        # 因此采用short數據類型轉換。現在我們得到的wave_data是一個一維的short類型的數組,但是因為我們的聲音文件是雙聲道的,
        # 因此它由左右兩個聲道的取樣交替構成:LRLRLRLR....LR(L表示左聲道的取樣值,R表示右聲道取樣值)。修改wave_data的sharp之后:

        # 將wave_data數組改為2列,行數自動匹配。在修改shape的屬性時,需使得數組的總長度不變。v
        wave_data = wave_data.T
        # 將波形數據轉換為數組
        freq = [self.df*self.collect_point_num for n in range(0,self.collect_point_num)]
        # 通過取樣點數和取樣頻率計算出每個取樣的時間

3.劃分采樣位置,建立頻譜圖坐標系,根據采樣時間標記采樣點在頻譜圖上的位置

  • wave_data2保存聲音字節數組轉置后的結果,為列數為1存儲的數組
  • 固定第一位,劃分第二維區間從0一直掃描到行尾
  • 避免波形字節數組過長,利用numpy.fft.fft對壓縮為1/2的波形字節數組進行快速傅里葉變換,常規顯示采樣頻率一半的頻譜
  • 設定如果每個取樣點的取樣時間大于4000ms,分隔單位為10的波形數組顯示
 def Data_collection(self):
        wave_data = numpy.fromstring(self.str_data,dtype=numpy.short)
        # 根據聲道數和量化單位,將讀取的二進制數據轉換為一個可以計算的數組

        wave_data.shape = -1.2
        # -1代表左聲道,2代表右聲道
        # 通過fromstring函數將字符串轉換為數組,通過其參數dtype指定轉換后的數據格式,由于我們的聲音格式是以兩個字節表示一個取樣值,
        # 因此采用short數據類型轉換。現在我們得到的wave_data是一個一維的short類型的數組,但是因為我們的聲音文件是雙聲道的,
        # 因此它由左右兩個聲道的取樣交替構成:LRLRLRLR....LR(L表示左聲道的取樣值,R表示右聲道取樣值)。修改wave_data的sharp之后:

        # 將wave_data數組改為2列,行數自動匹配。在修改shape的屬性時,需使得數組的總長度不變。v
        wave_data = wave_data.T
        # 將波形數據轉換為數組
        freq = [self.df*self.collect_point_num for n in range(0,self.collect_point_num)]
        # 通過取樣點數和取樣頻率計算出每個取樣的時間
        wave_data2 = wave_data[0][self.start:self.start+self.collect_point_num]
        c = numpy.fft.fft(wave_data2)*2/self.collect_point_num
        # 常規顯示采樣頻率一半的頻譜
        d = int(len(c)/2)
        while freq[0] > 4000:
            d -= 10
            pylab.plot(freq[:d-1],abs(c[:d-1]),"r")
            pylab.show()

python console:頻譜的時間段劃分似乎造成了誤差,導致統計結果趨于集中

fft.png

Test1:打印波形字節數組長度以及每個采樣點采樣花費的時間

 def Data_collection(self):
        wave_data = numpy.fromstring(self.str_data,dtype=numpy.short)
        # 根據聲道數和量化單位,將讀取的二進制數據轉換為一個可以計算的數組

        wave_data.shape = -1, 2
        # -1代表左聲道,2代表右聲道
        # 通過fromstring函數將字符串轉換為數組,通過其參數dtype指定轉換后的數據格式,由于我們的聲音格式是以兩個字節表示一個取樣值,
        # 因此采用short數據類型轉換。現在我們得到的wave_data是一個一維的short類型的數組,但是因為我們的聲音文件是雙聲道的,
        # 因此它由左右兩個聲道的取樣交替構成:LRLRLRLR....LR(L表示左聲道的取樣值,R表示右聲道取樣值)。修改wave_data的sharp之后:

        # 將wave_data數組改為2列,行數自動匹配。在修改shape的屬性時,需使得數組的總長度不變。v
        wave_data = wave_data.T
        # 將波形數據轉換為數組
        freq = [self.df*self.collect_point_num for n in range(0,self.collect_point_num)]
        print(freq)
        # 通過取樣點數和取樣頻率計算出每個取樣的時間
        wave_data2 = wave_data[0][self.start:self.start+self.collect_point_num]
        c = numpy.fft.fft(wave_data2)*2/self.collect_point_num
        d = int(len(c)/2)
        print(d)

python console

print.jpg

Test2: 原來是設定的采樣時間過小,修改統計條件為 freq[0] > 44101

    def Data_collection(self):
        wave_data = numpy.fromstring(self.str_data,dtype=numpy.short)
        # 根據聲道數和量化單位,將讀取的二進制數據轉換為一個可以計算的數組

        wave_data.shape = -1, 2
        # -1代表左聲道,2代表右聲道
        # 通過fromstring函數將字符串轉換為數組,通過其參數dtype指定轉換后的數據格式,由于我們的聲音格式是以兩個字節表示一個取樣值,
        # 因此采用short數據類型轉換。現在我們得到的wave_data是一個一維的short類型的數組,但是因為我們的聲音文件是雙聲道的,
        # 因此它由左右兩個聲道的取樣交替構成:LRLRLRLR....LR(L表示左聲道的取樣值,R表示右聲道取樣值)。修改wave_data的sharp之后:

        # 將wave_data數組改為2列,行數自動匹配。在修改shape的屬性時,需使得數組的總長度不變。v
        wave_data = wave_data.T
        # 將波形數據轉換為數組
        freq = [self.df*self.collect_point_num for n in range(0,self.collect_point_num)]
        print(freq)
        # 通過取樣點數和取樣頻率計算出每個取樣的時間
        wave_data2 = wave_data[0][self.start:self.start+self.collect_point_num]
        c = numpy.fft.fft(wave_data2)*2/self.collect_point_num
        d = int(len(c)/2)
        print(d)

        while freq[0] > 44101:
            d -= 0.1
            pylab.plot(freq[:d-1],abs(c[:d-1]),"r")
            pylab.show()

python console采樣的分布過于密集,不適合用頻譜圖進行統計


pinpu.png

Test3:使用波形圖,分別用subplot211與subplot212標識左右聲道的波形

import wave
import pyaudio
import numpy
from matplotlib import pylab


#打開wav文檔,文件路徑根據需要修改
wf = wave.open("F:\\work\\war.wav","rb")

#創建PyAudio對象
p = pyaudio.PyAudio()


class Audio(object):

    def __init__(self):
        self.channels = wf.getnchannels()
        # 返回音頻通道數
        self.rate = wf.getframerate()
        # 返回采樣頻率。
        self.format = p.get_format_from_width(wf.getsampwidth())
        # 返回指定寬度的PortAudio格式常量。
        self.stream = p.open(format=self.format,channels=self.channels,rate=self.rate,output=True)
        # 使用所需的音頻參數在所需設備上打開一個流
        self.nframes = wf.getnframes()
        # 返回音頻幀數
        self.collect_point_num = 44100
        # 采樣點數,修改采樣點數和起始位置進行不同位置和長度的音頻波形分析
        self.start = 0
        # 開始采樣位置
    def read_data(self):
        self.str_data = wf.readframes(self.nframes)
        # 讀取聲音數據,傳遞一個參數指定需要讀取的長度(以取樣點為單位),readframes返回的是二進制數據(即bytes數組)
        # print(self.str_data)
        wf.close()

    def convert_data(self):
        self.df = self.rate / (self.collect_point_num - 1)
        # print(self.df)
        # 使用全局采樣頻率除以全局采樣點數,以求出幀率
    def Data_collection(self):
        wave_data = numpy.fromstring(self.str_data,dtype=numpy.short)
        # 根據聲道數和量化單位,將讀取的二進制數據轉換為一個可以計算的數組

        wave_data.shape = -1, 2
        # -1代表左聲道,2代表右聲道
        # 通過fromstring函數將字符串轉換為數組,通過其參數dtype指定轉換后的數據格式,由于我們的聲音格式是以兩個字節表示一個取樣值,
        # 因此采用short數據類型轉換。現在我們得到的wave_data是一個一維的short類型的數組,但是因為我們的聲音文件是雙聲道的,
        # 因此它由左右兩個聲道的取樣交替構成:LRLRLRLR....LR(L表示左聲道的取樣值,R表示右聲道取樣值)。修改wave_data的sharp之后:

        # 將wave_data數組改為2列,行數自動匹配。在修改shape的屬性時,需使得數組的總長度不變。v
        wave_data = wave_data.T
        # 將波形數據轉換為數組
        freq = [self.df*self.collect_point_num for n in range(0,self.collect_point_num)]
        # print(freq)
        # 通過取樣點數和取樣頻率計算出每個取樣的時間
        wave_data2 = wave_data[0][self.start:self.start+self.collect_point_num]
        c = numpy.fft.fft(wave_data2)*2/self.collect_point_num
        d = int(len(c)/2)
        # print(d)

        while freq[0] > 44101:
            d -= 20
            pylab.plot(freq[:d-1],c[:d-1],"r")
            pylab.show()


    def wavread(self):
        wavfile = wf
        params = wavfile.getparams()
        framesra, frameswav = params[2], params[3]
        datawav = wavfile.readframes(frameswav)
        wavfile.close()
        datause = numpy.fromstring(datawav, dtype=numpy.short)
        datause.shape = -1, 2
        datause = datause.T
        time = numpy.arange(0, frameswav) * (1.0 / framesra)
        return datause, time


    def work(self):
        self.read_data()
        self.convert_data()
        self.Data_collection()

if __name__ == '__main__':
    aduio = Audio()
    # aduio.work()

    wavdata, wavtime = aduio.wavread()
    pylab.title("Night.wav's Frames")
    pylab.subplot(211)
    pylab.plot(wavtime, wavdata[0], color='green')
    pylab.subplot(212)
    pylab.plot(wavtime, wavdata[1])
    pylab.show()

python console

wave.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容