使用Raspberry Pi和Arduino Uno構建波表合成器

不久前,我們研究了如何使用Raspberry Pi的I2C匯流排來控制Arduino Uno,以及如何使用它來輸出類比電壓。

令人興奮的是,完成這些項目後,您就已經掌握構建一個簡單合成器的大部分方法了。

並且一家名叫dzl的製造商已經發佈了一組名為the_synth的Arduino庫,我們可以馬上安裝並使用該庫。

那麼,就讓我們來一起完成這個項目吧!現在,我們來構建一個波表合成器。

“波表”合成器是什麼意思?

一個波表合成器可以將一系列簡單波形以不同電壓電平波表檔的形式存儲在記憶體中。

合成器將這些電壓以不同的速率輸出,來產生相應頻率的音調。

我們只需要存儲每種波形的一個週期,這是一種可以高效節省記憶體的方法,因此非常適用於記憶體有限的簡單微控制器。

在the_synth裡,您可以在tables.h文件中找到波表。您也可以透過編輯該檔來創建自己的波表。

然後,我們透過把Arduino Uno的PWM硬體連接到低通濾波器,將記憶體中的這些數值轉換為類比電壓,如之前的專案中所描述的那樣。

所需零件

Raspberry Pi
一個GPIO擴展板
一個無焊麵包板
一個Arduino Uno
一個1k歐姆電阻
一個10nF電容
一個220uF電容
一個有源揚聲器

最好不要使用昂貴的有源揚聲器來構建和測試DIY合成器或音訊項目。雖然這樣的電路不一定會損壞您的東西,但是為什麼要冒險呢?

我一直使用的都是從二手商店花5美元購買的大型舊固態高保真音響。一些舊的電腦揚聲器也是不錯的選擇。

安裝範例

The_synth附帶了許多範例用於演示其使用方法,我們可以直接使用它們。在深入研究代碼之前,我們先試一下其中一個範例。

如果想要在您的Arduino IDE中安裝庫和其中的範例,請前往 https://github.com/dzlonline/the_synth並點擊在頁面右上方標有“Code”的綠色按鈕。

點擊“Download ZIP”,並保存該檔。

現在打開Arduino IDE。在“Sketch”現在打開Arduino IDE。在“Include Library”處,然後點擊“Add .ZIP library”。

找到我們之前保存的.zip檔,選擇它並點擊“OK”。

您現在已經成功將the_synth安裝到了您的IDE中,並且可以從功能表中訪問庫和範例了。讓我們試試any_hertz範例,它可以迴圈遍歷四種不同的頻率。

您可以打開“File”功能表,將滑鼠停留在“Examples”處,然後滾動到底部至“the_synth-master”找到該範例。將滑鼠停留在該選擇上,並點擊“any_hertz”來打開草圖。

現在透過USB連接Arduino Uno,請再次確認您選擇了Arduino Uno,確認後上傳草圖。

接線

現在,我們需要將Arduino Uno連接到一個簡單的低通濾波器,就像之前構建項目中所做的那樣,然後將它連接到我們的揚聲器。

首先,從連接電源引腳開始:


將引腳11(即PWM引腳)連接到麵包板的中間。我們還需要接地來構建低通濾波器,因此將接地引腳連接到負電源軌。

現在構建低通濾波器。將1k電阻連接到PWM引腳,然後將10nF電容連接到電阻的另一端和接地軌,如下所示:

現在我們需要一個耦合電容器,以確保不會透過任何直流電。需要記住的一點是電解電容器是有極性的,因此請將陽極(較長端)連接到低通濾波器,另一端連接到麵包板下方。

將揚聲器連接到耦合電容器的陰極,如有需要可以接地。

如果您已經正確完成了所有連接,您現在將會聽到一組包含四種音調的聲音。

使用I2C 和Raspberry Pi控制合成器

您現在可以聽到自己所構建的波表合成器所發出的聲音了,是不是很酷?

但只是一遍又一遍地聽著這四種音調並不是很有趣。我們想要能夠使用Raspberry Pi對其進行控制。

我們可以在I2C匯流排上使用Arduino的Wire庫和Python中的SMBus模組(我們之前介紹過)來完成此操作。

對Arduino Uno程式設計

我們真正需要做的是將這個any_hertz範例和之前編寫的I2C程式整合在一起,以播放我們發送的音符。
我們先導入I2C和synth庫。
#include <Wire.h>
#include <synth.h>

每個Arduino草圖都需要一個設置函數,在我們的設置函數中,需要完成三件事情:作為從機加入I2C匯流排;初始化我們的合成器;以及在收到I2C指令時調用另一個函數。
我們可以按照如下所示編寫:
void setup() {

edgar.begin(); //-Start up a synth
edgar.setupVoice(0,TRIANGLE,60,ENVELOPE1,127,64); //-Set up voice 0

Wire.begin(0x8); // Join the I2C Bus as a slave at address 0x8
Wire.onReceive(readInstruction); // On
}

現在我們需要編寫readInstruction()函數。該函數用於從I2C匯流排讀取一個數位。如果讀取了0,那麼將不播放任何東西。如果讀取了1到8之間的數字,那麼將會播放A1到A2之間的一個音符。
void readInstruction(int bitstream) {
byte option = Wire.read();
switch (option) {
case 0:
edgar.setFrequency(0, 0.0); // Play nothing
edgar.trigger(0);
break;
case 1:
edgar.setFrequency(0, 55.0); // Play A1
edgar.trigger(0);
break;
case 2:
edgar.setFrequency(0, 61.74); // Play B1
edgar.trigger(0);
break;
case 3:
edgar.setFrequency(0, 65.41); // Play C2
edgar.trigger(0);
break;
case 4:
edgar.setFrequency(0, 73.42); // Play D2
edgar.trigger(0);
break;
case 5:
edgar.setFrequency(0, 82.41); // Play E2
edgar.trigger(0);
break;
case 6:
edgar.setFrequency(0, 87.31); // Play F2
edgar.trigger(0);
break;
case 7:
edgar.setFrequency(0, 98.0); // Play G2
edgar.trigger(0);
break;
case 8:
edgar.setFrequency(0, 110.0); // Play A2
edgar.trigger(0);
break;
}
}

最後,每個Arduino草圖都需要一個迴圈函數。但實際上我們沒有需要迴圈運行的工作,所以我們只在其中寫入一個sleep函數。
void loop() {
sleep(10000);
}

保存該草圖,並將其上傳到您的Arduino Uno。

連接I2C匯流排

這部分內容與我們在之前的文章中所介紹的完全相同:將Raspberry Pi的SDA引腳連接到Arduino Uno的A4引腳,將SCL引腳連接到A5引腳。

播放一首簡單的歌曲

現在讓我們用該項目來播放歌曲《Mary Had a Little Lamb》。
這首歌有兩種不同長度的音符:四分音符和半音符(或二分音符)。我們現在來編寫兩個快速函數來播放這些音符。
我們需要計時功能,因此在這裡導入sleep函數。
from time import sleep

但是中間需要暫停多久?我們的歌曲是每分鐘122拍。根據該數值,我們可以計算出每個音符需要多少秒:四分音符是492微秒。我們在結尾處設置一段非常短的暫停,以便能夠在播放相同音調時分辨這些音符。
def playCrotchet(int pitch):
i2cbus.write_byte(pitch)
sleep(0.472)
i2cbus.write_byte(arduino, 0)
sleep(0.02)

現在我們來寫一個半音符函數:

def playMinim(int pitch):
i2cbus.write_byte(pitch)
sleep(0.964)
i2cbus.write_byte(arduino, 0)
sleep(0.02)

然後:

def playMary():
playCrotchet(E2)
playCrotchet(D2)
playCrotchet(C2)
playCrotchet(D2)
playCrotchet(E2)
playCrotchet(E2)
playMinim(E2)
playCrotchet(D2)
playCrotchet(D2)
playMinim(D2)
playCrotchet(E2)
playCrotchet(G2)
playMinim(G2)
playCrotchet(E2)
playCrotchet(D2)
playCrotchet(C2)
playCrotchet(D2)
playCrotchet(E2)
playCrotchet(E2)
playCrotchet(E2)
playCrotchet(E2)
playCrotchet(D2)
playCrotchet(D2)
playCrotchet(E2)
playCrotchet(D2)
playMinim(C2)

想要播放該歌曲,請輸入:

playMary()

多嘗試一些內容,看看有沒有什麼新的想法。

下一步該做什麼?

這是探索合成器構建方法的一個很好的開端:您還可以基於該內容製作一些很酷的低音線。
但是將所有音樂都寫成代碼是相當麻煩的,而且我們目前只有有限的音符和簡單的波形。
接下來如果可以使用圖形介面以及改變音符的音色不是會更好麼?
請繼續關注我們——我們將會很快進行這些內容的探索。