2014-10-28

[程式]用Squeak Smalltalk寫一個簡單的MIDI發聲程式

前一陣子買了61鍵的Casio XW-P1合成器來玩,但是都沒有嘗試與電腦連動。
最近突然想到,以前曾經接觸過Squeak Smalltalk,在它內建的class庫中,正好有SimpleMIDIPort這個class可以讓我們接收與發送MIDI event。
只要靠SimpleMIDIPort,我們就可以寫個簡單的程式,送些MIDI event到外接的MIDI設備,看看電腦可以對該MIDI設備做到什麼程度的控制。
於是筆者就在這裡記下一段簡單的Smalltalk程式碼,方便之後做些測試。

| midiPortNum midiPort |
midiPortNum := SimpleMIDIPort outputPortNumFromUser.
midiPort := SimpleMIDIPort openOnPortNumber: midiPortNum.
Transcript show: midiPortNum; cr; show: midiPort; cr.

midiPort midiCmd: 16rC0 channel: 0 byte: 90. "change program"

midiPort midiCmd: 16r90 channel: 0 byte: 60 byte: 16r7F.
(Delay forSeconds: 0.5) wait.
midiPort midiCmd: 16r80 channel: 0 byte: 60 byte: 16r0.
midiPort midiCmd: 16r90 channel: 0 byte: 60 byte: 16r7F.
(Delay forSeconds: 0.5) wait.
midiPort midiCmd: 16r80 channel: 0 byte: 60 byte: 16r0.
midiPort midiCmd: 16r90 channel: 0 byte: 67 byte: 16r7F.
(Delay forSeconds: 0.5) wait.
midiPort midiCmd: 16r80 channel: 0 byte: 67 byte: 16r0.
midiPort midiCmd: 16r90 channel: 0 byte: 67 byte: 16r7F.
(Delay forSeconds: 0.5) wait.
midiPort midiCmd: 16r80 channel: 0 byte: 67 byte: 16r0.
midiPort midiCmd: 16r90 channel: 0 byte: 69 byte: 16r7F.
(Delay forSeconds: 0.5) wait.
midiPort midiCmd: 16r80 channel: 0 byte: 69 byte: 16r0.
midiPort midiCmd: 16r90 channel: 0 byte: 69 byte: 16r7F.
(Delay forSeconds: 0.5) wait.
midiPort midiCmd: 16r80 channel: 0 byte: 69 byte: 16r0.
midiPort midiCmd: 16r90 channel: 0 byte: 67 byte: 16r7F.
(Delay forSeconds: 0.5) wait.
midiPort midiCmd: 16r80 channel: 0 byte: 67 byte: 16r0.

midiPort midiCmd: 16rC0 channel: 0 byte: 0. "reset program"
midiPort midiOutput: (#(16rFF) asByteArray). "system reset"

以上的程式範例會用120bpm的速度控制MIDI設備的Channel 0放送Twinkle Twinkle Little Star的前7個主音。

如果有現成的MIDI檔的話,也可用下面的程式開啟MIDI檔,並輸出到作業系統提供的MIDI裝置。

| file score scorePlayer midiPortNum |

file := FileStream fileNamed: 'C:\Windows\Media\flourish.mid'.
file binary.

score := ((MIDIFileReader new) readMIDIFrom: file) asScore.
file close.

scorePlayer := ScorePlayer onScore: score.

midiPortNum := SimpleMIDIPort outputPortNumFromUser.
scorePlayer openMIDIPort: midiPortNum.

scorePlayer reset.
scorePlayer startMIDIPlaying.
(Delay forSeconds: 60.0) wait.
scorePlayer stopMIDIPlaying.

之後想要測什麼就可以用這些template程式碼起頭了。