謝作如 陳益漳
DF的創客社區是學生們常常去的地方,在那里總能看到一些稀奇古怪的項目。這次學生看到的是一個用步進電機播放MIDI音樂的帖子,幾個步進電機居然能播放出各種音符,實在很酷。根據帖子的介紹和提供的代碼,我們很快發現其使用方式較老,并且需要Linux環境,操作不方便,兼容性也不好。于是,溫州中學創客空間也買了相關的設備,研究了一種新的MIDI播放器方案。
● 原理分析
首先需要了解一些原理。步進電機是將電脈沖信號轉變為角位移或線位移的開環控制電動機。它通過控制脈沖個數可以控制步進電機的角位移量,從而達到準確定位的目的;同時通過控制脈沖頻率可以控制電機轉動的速度和加速度。因為速度和角度可控,步進電機常用在3D打印機、激光雕刻機上。
步進電機運轉時會發出噪音,而這噪音具有一定的規律,其音高與當前電機轉速有直接關聯。如果給步進電機合適頻率的脈沖,步進電機就能夠按照一定的音高發出我們需要的聲音。經過反復實驗和參考資料,直接以標準A(440Hz)的頻率發送脈沖,并按照半音頻率之間相差的規律(也就是升一個八度頻率翻一倍),就可以發出對應的音。這一方式也適用于蜂鳴器等可以發出聲音的元件。
接下來需要了解點MIDI知識。MIDI即樂器數字接口,是編曲界使用最廣泛的數字音樂標準格式。MIDI的記譜方式和五線譜、簡譜有所不同,標準A在MIDI中是A4,每升八度(12個半音)就會加一(如A1升八度變成A2),具體如表1所示。

一個MIDI信號可以有多個字節的內容,但第一字節的內容格式是固定的。第一字節稱為狀態字節,表示對MIDI設備當前狀態的設定。狀態字節的指令高位(前四位)一定會大于或等于8(0b1000),而低位(后四位)表示控制的軌道(0x00~0x0F)。前四位代表的信息類型如表2所示。
● 用Arduino處理MIDI
使用Arduino的最大優勢是能夠找到很多開源的庫或者代碼。一個叫做MIDIUSB的庫可以直接將Arduino Leonardo板變成一個USB接口的MIDI設備。MIDIUSB庫中封裝了一個名稱為MidiUSB的類,它可以直接用MidiUSB.read()讀取MIDI信號,并返回一個midiEventPacket_t類的變量。庫中midiEventPacket_t的定義如圖1所示。
可以看到這個類(實際上只是結構體)中只存了3個字節的內容(header實際上是byte1的前4位,即第一字節的高位,表示當前指令)。之后需要的就是對讀取的MIDI信號進行處理。處理MIDI的部分同樣可以在一些論壇(建議找國外論壇)上找到,改改就能用了(如圖2)。

從這段代碼可以看出,在處理MIDI信號時要針對狀態字節的具體指令進行分類操作。實際上這段代碼只涉及了兩個會用到的操作:發聲和停止發聲。代碼中還使用了tone函數。tone在英語中原意是音調,在Arduino中的作用就是對一個端口發送一定頻率的脈沖。其原型為tone(pin, frequency[, duration]),即(在duration的時間內)向pin端口發送頻率為frequency Hz的脈沖。與之對應的還有停止發送脈沖的noTone()。tone函數的缺點在于不能同時對一個以上的端口發送信號。而采用分時系統(姑且稱之為“偽多線程”)就無法及時停止脈沖,會導致不同步,這可能與Leonardo的性能和分時系統本身的缺陷有關。
● 硬件搭建
播放設備使用了Arduino Leonardo和DFrobot出品的雙路步進電機驅動板和步進電機。播放設備的結構如圖3所示。
先將電機、驅動板、Leonardo組裝在一起。因為步進電機驅動板需要超過8V的電壓才能工作,所以先拿洞洞板焊了一個并聯4個電機的部件。同時4塊Leonardo使用串口通信來傳輸信號,就把通信的線一起焊在洞洞板上了(如圖4)。

將MIDI處理的代碼燒錄到各個Leonardo板子上。但是一號板子燒寫的代碼里要使用MIDIUSB,其他的板就直接用串口就行了。
試著用WIDI播放改編過的曲子“千本櫻”,大家居然都說挺好聽。電機振動居然能夠發出有規律的樂音,的確挺酷的,吸引了很多同學圍觀。視頻地址為http://www.bilibili.com/video/av4596330/。
在使用過程中我們還發現,要讓電機發出足夠響的聲音,最好做個共鳴腔——用小紙箱就行了。還要將電機用扎帶固定在木板上,因為電機發聲的音量和與之共振的物體大小有關(如圖5)。
完成基于步進電機的MIDI播放設備后,再對比原來論壇上的帖子,其在兼容性方面的確要好一些。原帖子介紹要在Linux下運行,而這個方案可以運行在任何操作系統下,只要能運行支持MIDI設備的音樂軟件,如WIDI。但這一方案也存在一些不足,如不能夠播放和弦、不夠直觀等。

如果對相關內容感興趣,請關注主持人博客。
參考文獻:
[1]ChoirBot,桌子上的迷你機械樂隊[J/OL].http://www.dfrobot.com.cn/community/thread-14112-1-1.html.
[2]步進電機_百度百科[EB/OL].http://baike.baidu.com/link?url=e6O45jcE2J1mb0n-yOxCr3sZv_HXz1uibWk7g2q-22xv_ccM8f7CughGI4AsDyYoOkvoK8kerpmrXyMGxCQNCq.
[3]Tone() + MIDI = ToneMIDISynth[J/OL].http://forum.arduino.cc/index.php?topic=79326.0.