從零開始寫自己的synthesizer vol.1正弦波

從零開始寫自己的synthesizer vol.1正弦波

來自專欄 Jekyll and Hydes Music Room11 人贊了文章

自從上個學期學了一點python之後自己的手就管不住了,cs課的final project做了一個非常蹩腳的rpg遊戲。做完我才恍然大悟,為啥我不能做個synthesizer呢?於是在假期動手寫了一個。喜歡的各位可以自己做做試試。

Disclaimer:用python做synthesizer十分低效而且非常麻煩!有多麻煩呢,麻煩到我都不想解釋有多麻煩了。所以大佬請不要噴我為什麼不用reaktor為什麼不用nyquist云云,和我差不多寫碼經驗的也請不要抱怨您這功能太爛了,畢竟這東西本身也不是正兒八經的project,就是自己寫寫玩玩,說不定哪一天就被我不小心刪了。(更重要的是,我本人啥都不會。)

需要的軟體等:

  1. python 3.6
  2. pyaudio module(不是必須,會說明為什麼比沒有要好)
  3. bumpy module
  4. scipy module
  5. audacity(看頻譜很方便,有別的就可以不用了)

DSP入門:

在寫碼之前我們先了解一些基本的信號處理知識。我們都知道我們所聽到的聲音的本質是空氣的震動傳入我們的耳朵,而這之中比較有規律的那些可以被我們的耳朵理解為音樂,有規律的重複可以體現出節奏,旋律和和聲等複雜的音樂現象。

一個標準的A440的波形圖。audacity中可以比較直觀的看波形

學過高數的同學會知道,一切的波都能通過傅立葉變換變成最簡單的正弦函數。至於具體為什麼,我們就不講了,這裡不是高數課堂。:D

那麼我們現在可以來看看這種最基礎的波的幾個要素:

振幅:

這是我們平常描述為聲音響度的玩意。波最上端和最下端的距離就是振幅。振幅越大,聲音就越響。

頻率:

頻率我覺得也不用說太多,頻率越高的聲音,會有越高的音調。

採樣率:

我們在代表聲音信號的時候,並不能把所有的信息都讀取為電子信號。聰明的電子工程師們通過實驗發現我們只要在足夠快的速度記錄下來在這一刻信號的值是什麼,我們的耳朵就會interpret成原來的聲音。這個過程我們叫做採樣,我們每一秒採樣的次數就是採樣率啦。一般音頻文件和唱片用的都是44100Hz。

dsp的部分就這麼結束了!有什麼不會的!我會在後面解釋的!真的!


準備工作:

在產生波形之前,我們需要先在python里import需要的幾個module:

import numpy as npimport pyaudiofrom scipy import signalfrom scipy.io.wavfile import write

numpy:

這個module是我們最重要的功能之一。大家可能都學過list,我們需要的這個numpy可以給一個更高級版本的list,array。這個類擁有一些非常強大的計算功能,而且可以非常方便地在我們需要的不同數據類型之間轉換。array的一個特點是broadcasting,即只要你對一個array做任意運算,所有的元素都會同時做同樣的運算。

pyaudio:

這個module是用來在python里播放聲音的。我們分析音頻最細緻的方法應該是檢查這一段音頻的波形,但是到了後期光靠看波形是做不到想像音頻聽起來是什麼樣的,所以我們可以加入這個module來聽它。

scipy:

這個module也非常重要,我們需要這個module的一些功能來完成波形的編輯。我們同樣需要這個module來將一段音頻保存為wav文件。

我們現在已經準備好接受python的洗禮了!


生產波形!

產生正弦函數的方式有很多,而我選擇了一種並不是特別好的方法。如果你能想出更好的方法,一定不要用我這一種。

我先產生了一個長度為「時間(秒)*採樣率」的array,然後求其中每一個數值的sin值。這時我們就可以用array的broadcast功能了。freq是我們需要的正弦波頻率。

T=5samplingRate=44100freq=440samplesPerSec = np.arange(samplingRate * T)carrier = np.sin(2*np.pi*samplesPerSec*freq/samplingRate)

這樣我們得到的carrier就是一個滿載著正弦波的array了。但是為了讓電腦能夠把它播放出來,我們還需要轉換一下它的格式。

amp1=0.3amp2=1sound = np.int16(carrier * 32767*amp1)*amp2

具體為啥我乘了個32767?這個你長大之後就會懂了。可能細心的朋友已經發現了,這個0.3是個什麼鬼?它的存在是為了防止你的音響失真。想試試的話,可以保護好耳朵和音響之後把0.3改成1或者0.5。

然後我們準備把這一段聲音播放出來。首先我們把這段聲音存到本地。這就要用到我們剛剛import的write方程。這個文檔是wav,我們可以用audacity打開,然後會看到的就是之前我們唯一一張圖的那個樣子。

write("A440.wav",samplingRate,sound)

然後,如果我們想要即時在python里聽的話,pyaudio就派上用場了。

p = pyaudio.PyAudio()stream = p.open(format=pyaudio.paInt16, channels=1, rate=44100, output=True)stream.write(sound)

這樣我們就能播放聲音了。


說在後面

一些可以嘗試的有趣東西:

在之前我們的0.3這個常數那裡,有探索精神的朋友可能就已經試過把這個數調整到1以上的數字了吧。大家不妨試試調整到很高的值,看看這些值聽起來是什麼樣的,波形看起來是什麼樣的。

這是amp1=amp2=1

這是amp1=1,amp2=1.5,是不是已經開始有趣了

amp1=1.5,amp2=1

上面這些情況是我們下面幾期可能會扯到的distortion。我們可以很直觀地發現這些音的振幅太大了,多餘的部分要麼被削掉了,要麼被折回來了。上面那種就是我們常見的搖滾中的失真,而下面這個是一種類似的稱為mirror的失真效果。我們同時也不難發現被嚴重失真的正弦波其實和方波看起來挺像的。那麼我們下一期前的作業(???)就做這個吧!感興趣的朋友可以自己自己探究一下用類似正弦波的方式如何製作方波(三角波也行),用scipy.signal這個類。


推薦閱讀:

乾貨篇:Python基礎教程之shell編程資料大全
Python參數傳遞,既不是傳值也不是傳引用
獲取本機的上網IP
【翻譯】《利用Python進行數據分析·第2版》第11章(中二) 時間序列
word文檔(docx)的讀取和寫入:docx模塊

TAG:DSP數字信號處理 | Python | 音樂 |