2019年11月25日

用Micro:bit的Java Script玩音樂

在Micro:bit的程式積木中預設就有演奏音樂的功能,不過如果要自己透過積木來編寫樂譜演奏音樂的話,積木得拉得非常長一串,實在難以編寫。於是我轉向利用Java Script開發,希望把樂曲的簡譜以陣列方式書寫,再以程式讀取簡譜的陣列來播放。

而在實作的過程中,我又遇到音高和頻率的問題,實在不想直接指定哪個音名需播放什麼頻率,於是我也轉向用程式直接計算頻率,這麼一來編寫出來的程式居然有了新功能,我能夠指定要以什麼調性來演奏。例如同樣一篇簡譜的歌曲,我可以任意指定以C大調演奏,或是以D大調、C小調演奏,完全不須更動簡譜。
而且除了演奏簡譜之外,你也可以輸入一串如圓周率的數字,這樣也能演奏出歌曲。或是DNA或蛋白質的序列都可以演奏的。

我在十幾年前寫過的兩篇序列產生音樂的介紹文【聽蛋白質唱歌】、【你也可以讓DNA唱歌】,現在你也可以用這方式自己寫程式產生音樂了。


以下就以頻率計算的部分來說明,詳細的專案我都已經錄製成影片,另外影片中使用的簡報也在最後,同時程式碼也附在簡報當中。





以下幾張簡報要說明的是下面這串程式是怎麼寫出來的,這是最基本的程式,細節都可以看影片的說明
let scale = [0, 2, 4, 5, 7, 9, 11]
let start = 60

input.onButtonPressed(Button.A, function () {
   let sound = 1
   let playtime = 150
   let midiNumber = start + scale[(Math.abs(sound - 1)) % 7] + 12 * Math.floor((sound - 1) / 7)
   let frequency = 440 * (1.0594 ** (midiNumber - 69))
   music.playTone(frequency, playtime)
})

只要改變sound的數值,就可以產生不同音高的聲音。以sound =1 就是發出Do的音,若是 =2則是Re,以此類推。而高音的Do則是 sound = 8,低音的Do則是 sound = -1。
要演奏音樂,就只是讓sound依序等於一串string或是陣列裡的某個數字就可以,而那個數字可能是pi的一個位數或是蛋白質序列轉成ASCII Code,又或是正常樂曲裡的一個音符。


第一,要知道的基本知識,國際化標準組織訂出了A6(中音La) 的頻率是440Hz,此為標準音高,其他音高是由此標準音高去計算出來的

第二,每個八度音之間的關係是兩倍的頻率,例如中音La是440Hz,而高音La是880Hz,低音La是220Hz。

第三,八度音之間有12個半音


每個半音是前一個半音的常數倍,2^(1/12)倍, 約等於 1.059倍。只要知道標準音高是440,則下一個半音的頻率就是440x1.059,再下一個就是440*1.059*1.059,以此類推



而在MIDI標準中,中音La的Midi number訂為69,因此我們可以用這個數字來參照。例如中音Si是71,和69相差兩個半音,所以知道Si的頻率是440*(1.059)^(71-69)



但又有下一個問題,我怎麼知道一個音的midi number呢?同樣可以用換算的
  1. 規定好C大調的鍵盤順序。大調的音階都是按鋼琴鍵盤的白鍵,把Do當作第0鍵,則Re Mi....依序為 2  4  5  7 9  11鍵。也就是鍵盤順序 =[ 0,2,4,5,7,9,11]
  2. 已知Do的Midi number是60

用上面這兩個小知識就可以給簡譜的數字,換算出Midi Number了。例如給簡譜數字2,這是Re,Midi Number就是60再加上鍵盤順序的第(2-1)項(註:鍵盤順序的第一個字稱為第0項)

如果是給簡譜數字6,Midi Number就是60加上鍵盤順序的第(6-1)項 = 60+9 =69


接下來是高音和低音的簡譜表示法,分別用不同的邏輯寫成,高音是加上7,低音則是前面加負號。
然後用這個公式就可以得到Midi Number
midiNumber = start + scale[(Math.abs(sound - 1)) % 7] + 12 * Math.floor((sound - 1) / 7)



最後就可以寫出程式,給予簡譜數字後,讓程式自動計算對應的頻率。
而最開始所說,可以任意更改成小調或是D大調等,也只是修改那個對應的鍵盤順序和起始的Midi Number而已。所有完整的程式在上述簡報的最後一頁,全選複製貼到Makecode的編輯器就可以。

如果是C大調,就是start = 60 , scale = [0, 2, 4, 5, 7, 9, 11]
如果是D大調,就是start = 62 , scale = [0, 2, 4, 5, 7, 9, 11] (起始音階換成 D這個音)
如果是C小調,就是start = 60 , scale = [0, 2, 3, 5, 7, 8, 10] (小調按的鍵盤順序和大調不同)