<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            聲音的輸入輸出?

            在本章我們將學習如何讀寫WAV文件,如何利用聲卡實時地進行聲音的輸入輸出。標準的Python已經支持WAV文件的讀寫,而實時的聲音輸入輸出需要安裝pyAudio(http://people.csail.mit.edu/hubert/pyaudio)。最后我們還將看看如何使用pyMedia(http://pymedia.org)進行Mp3的解碼和播放。

            掌握了上面的基礎知識之后,就可以做許多有趣的聲效處理的算法實驗了。聲效處理方面的內容將在以后的章節詳細介紹。

            讀寫Wave文件?

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

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

            例如CD中所儲存的聲音信號是雙聲道、44.1kHz、16bit。

            如果你需要自己錄制和編輯聲音文件,推薦使用Audacity(http://audacity.sourceforge.net),它是一款開源的、跨平臺、多聲道的錄音編輯軟件。在我的工作中經常使用Audacity進行聲音信號的錄制,然后再輸出成WAV文件供Python程序處理。

            讀Wave文件?

            下面讓我們來看看如何在Python中讀寫聲音文件:

             1
             2
             3
             4
             5
             6
             7
             8
             9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            24
            25
            26
            27
            28
            29
            30
            # -*- coding: utf-8 -*-
            import wave
            import pylab as pl
            import numpy as np
            
            # 打開WAV文檔
            f = wave.open(r"c:\WINDOWS\Media\ding.wav", "rb")
            
            # 讀取格式信息
            # (nchannels, sampwidth, framerate, nframes, comptype, compname)
            params = f.getparams()
            nchannels, sampwidth, framerate, nframes = params[:4]
            
            # 讀取波形數據
            str_data = f.readframes(nframes)
            f.close()
            
            #將波形數據轉換為數組
            wave_data = np.fromstring(str_data, dtype=np.short)
            wave_data.shape = -1, 2
            wave_data = wave_data.T
            time = np.arange(0, nframes) * (1.0 / framerate)
            
            # 繪制波形
            pl.subplot(211) 
            pl.plot(time, wave_data[0])
            pl.subplot(212) 
            pl.plot(time, wave_data[1], c="g")
            pl.xlabel("time (seconds)")
            pl.show()
            
            _images/wave_pyaudio_01.png

            WindowsXP的經典"叮"聲的波形

            首先載入Python的標準處理WAV文件的模塊,然后調用wave.open打開wav文件,注意需要使用"rb"(二進制模式)打開文件:

            import wave
            f = wave.open(r"c:\WINDOWS\Media\ding.wav", "rb")
            

            open返回一個的是一個Wave_read類的實例,通過調用它的方法讀取WAV文件的格式和數據:

            • getparams:一次性返回所有的WAV文件的格式信息,它返回的是一個組元(tuple):聲道數, 量化位數(byte單位), 采樣頻率, 采樣點數, 壓縮類型, 壓縮類型的描述。wave模塊只支持非壓縮的數據,因此可以忽略最后兩個信息:

              params = f.getparams()
              nchannels, sampwidth, framerate, nframes = params[:4]
              
            • getnchannels, getsampwidth, getframerate, getnframes等方法可以單獨返回WAV文件的特定的信息。

            • readframes:讀取聲音數據,傳遞一個參數指定需要讀取的長度(以取樣點為單位),readframes返回的是二進制數據(一大堆bytes),在Python中用字符串表示二進制數據:

              str_data = f.readframes(nframes)
              

            接下來需要根據聲道數和量化單位,將讀取的二進制數據轉換為一個可以計算的數組:

            wave_data = np.fromstring(str_data, dtype=np.short)
            

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

            wave_data.shape = -1, 2
            

            將其轉置得到:

            wave_data = wave_data.T
            

            整個轉換過程如下圖所示:

            最后通過取樣點數和取樣頻率計算出每個取樣的時間:

            time = np.arange(0, nframes) * (1.0 / framerate)
            

            寫Wave文件?

            寫WAV文件的方法和讀類似:

             1
             2
             3
             4
             5
             6
             7
             8
             9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            # -*- coding: utf-8 -*-
            import wave
            import numpy as np
            import scipy.signal as signal
            
            framerate = 44100
            time = 10
            
            # 產生10秒44.1kHz的100Hz - 1kHz的頻率掃描波
            t = np.arange(0, time, 1.0/framerate)
            wave_data = signal.chirp(t, 100, time, 1000, method='linear') * 10000
            wave_data = wave_data.astype(np.short)
            
            # 打開WAV文檔
            f = wave.open(r"sweep.wav", "wb")
            
            # 配置聲道數、量化位數和取樣頻率
            f.setnchannels(1)
            f.setsampwidth(2)
            f.setframerate(framerate)
            # 將wav_data轉換為二進制數據寫入文件
            f.writeframes(wave_data.tostring())
            f.close()
            

            10-12行通過調用scipy.signal庫中的chrip函數,產生長度為10秒、取樣頻率為44.1kHz、100Hz到1kHz的頻率掃描波。由于chrip函數返回的數組為float64型,需要調用數組的astype方法將其轉換為short型。

            18-20行分別設置輸出WAV文件的聲道數、量化位數和取樣頻率,當然也可以調用文件對象的setparams方法一次性配置所有的參數。最后21行調用文件的writeframes方法,將數組的內部的二進制數據寫入文件。writeframes方法會自動的更新WAV文件頭中的長度信息(nframes),保證其和真正的數據數量一致。

            用pyAudio播放和錄音?

            通過上一節介紹的讀寫聲音文件的方法,我們可以離線處理已經錄制好的聲音。不過更酷的是我們可以通過pyAudio庫從聲卡讀取聲音數據,處理之后再寫回聲卡,這樣就可以在電腦上實時地輸入、處理和輸出聲音數據。想象一下,我們可以做一個小程序,讀取麥克風的數據;加上回聲并和WAV文件中的數據進行混合;最后從聲卡輸出。這不就是一個Karaoke的原型么。

            pyAudio是開源聲音庫PortAudio( http://www.portaudio.com )的Python綁定,目前它只支持阻塞式的輸入輸出模式。所謂阻塞式就是需要用戶的程序主動地去讀寫輸入輸出流。雖然阻塞式在功能上有所局限,但是由于編程比較簡單,非常適合一些處理聲音的腳本程序開發。

            播放?

            下面先來看看如何用pyAudio播放聲音。

             1
             2
             3
             4
             5
             6
             7
             8
             9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            24
            # -*- coding: utf-8 -*-
            import pyaudio
            import wave
            
            chunk = 1024
            
            wf = wave.open(r"c:\WINDOWS\Media\ding.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.close()
            p.terminate()
            

            這段程序首先根據WAV文件的量化格式、聲道數和取樣頻率,分別配置open函數的各個參數,然后循環從WAV文件讀取數據,寫入用open函數打開的聲音輸出流。我們看到17-20行的while循環沒有任何等待的代碼。因為pyAudio使用阻塞模式,因此當底層的輸出數據緩存沒有空間保存數據時,stream.write會阻塞用戶程序,直到stream.write能將數據寫入輸出緩存。

            PyAudio類的open函數有許多參數:

            • rate - 取樣頻率
            • channels - 聲道數
            • format - 取樣值的量化格式 (paFloat32, paInt32, paInt24, paInt16, paInt8 ...)。在上面的例子中,使用get_format_from_width方法將wf.sampwidth()的返回值2轉換為paInt16
            • input - 輸入流標志,如果為True的話則開啟輸入流
            • output - 輸出流標志,如果為True的話則開啟輸出流
            • input_device_index - 輸入流所使用的設備的編號,如果不指定的話,則使用系統的缺省設備
            • output_device_index - 輸出流所使用的設備的編號,如果不指定的話,則使用系統的缺省設備
            • frames_per_buffer - 底層的緩存的塊的大小,底層的緩存由N個同樣大小的塊組成
            • start - 指定是否立即開啟輸入輸出流,缺省值為True

            錄音?

            從聲卡讀取數據和寫入數據一樣簡單,下面我們用一個簡單的聲音監測小程序來展示一下如何用pyAudio讀取聲音數據。

             1
             2
             3
             4
             5
             6
             7
             8
             9
            10
            11
            12
            13
            14
            15
            16
            17
            18
            19
            20
            21
            22
            23
            24
            25
            26
            27
            28
            29
            30
            31
            32
            33
            34
            35
            36
            37
            38
            39
            40
            41
            42
            43
            44
            45
            46
            47
            48
            49
            50
            51
            52
            53
            54
            55
            56
            57
            58
            59
            # -*- coding: utf-8 -*-
            from pyaudio import PyAudio, paInt16 
            import numpy as np 
            from datetime import datetime 
            import wave 
            
            
            # 將data中的數據保存到名為filename的WAV文件中
            def save_wave_file(filename, data): 
                wf = wave.open(filename, 'wb') 
                wf.setnchannels(1) 
                wf.setsampwidth(2) 
                wf.setframerate(SAMPLING_RATE) 
                wf.writeframes("".join(data)) 
                wf.close() 
            
            
            
            NUM_SAMPLES = 2000      # pyAudio內部緩存的塊的大小
            SAMPLING_RATE = 8000    # 取樣頻率
            LEVEL = 1500            # 聲音保存的閾值
            COUNT_NUM = 20          # NUM_SAMPLES個取樣之內出現COUNT_NUM個大于LEVEL的取樣則記錄聲音
            SAVE_LENGTH = 8         # 聲音記錄的最小長度:SAVE_LENGTH * NUM_SAMPLES 個取樣
            
            # 開啟聲音輸入
            pa = PyAudio() 
            stream = pa.open(format=paInt16, channels=1, rate=SAMPLING_RATE, input=True, 
                            frames_per_buffer=NUM_SAMPLES) 
            
            save_count = 0 
            save_buffer = [] 
            
            while True: 
                # 讀入NUM_SAMPLES個取樣
                string_audio_data = stream.read(NUM_SAMPLES) 
                # 將讀入的數據轉換為數組
                audio_data = np.fromstring(string_audio_data, dtype=np.short) 
                # 計算大于LEVEL的取樣的個數
                large_sample_count = np.sum( audio_data > LEVEL ) 
                print np.max(audio_data) 
                # 如果個數大于COUNT_NUM,則至少保存SAVE_LENGTH個塊
                if large_sample_count > COUNT_NUM: 
                    save_count = SAVE_LENGTH 
                else: 
                    save_count -= 1 
            
                if save_count < 0: 
                    save_count = 0 
            
                if save_count > 0: 
                    # 將要保存的數據存放到save_buffer中
                    save_buffer.append( string_audio_data ) 
                else: 
                    # 將save_buffer中的數據寫入WAV文件,WAV文件的文件名是保存的時刻
                    if len(save_buffer) > 0: 
                        filename = datetime.now().strftime("%Y-%m-%d_%H_%M_%S") + ".wav" 
                        save_wave_file(filename, save_buffer) 
                        save_buffer = [] 
                        print filename, "saved" 
            

            此程序一開頭是一系列的全局變量,用來配置錄音的一些參數:以SAMPLING_RATE為采樣頻率,每次讀入一塊有NUM_SAMPLES個采樣的數據塊,當讀入的采樣數據中有COUNT_NUM個值大于LEVEL的取樣的時候,將數據保存進WAV文件,一旦開始保存數據,所保存的數據長度最短為SAVE_LENGTH個塊。WAV文件以保存時的時刻作為文件名。

            從聲卡讀入的數據和從WAV文件讀入的類似,都是二進制數據,由于我們用paInt16格式(16bit的short類型)保存采樣值,因此將它自己轉換為dtype為np.short的數組。

            用pyMedia播放Mp3?

            首頁目錄

            上一篇文章

            場景窗口

            下一篇文章

            數字信號系統

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线