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程式碼起頭了。

2014-08-07

[設定]在VMWare中的Debian wheezy要如何啟動3D加速?

最近常常需要雙開Windows跟Linux來開發程式,考量到Windows會用到一些要求效能以及很會吃記憶體的工具,於是決定用Windows做為Host OS,而讓Linux做為VMWare中的Guest OS。
個人以往在VMWare中安裝Ubuntu是沒有什麼問題,只要裝好VMWare tools就可以讓Ubuntu直接享受到最完善的硬體加速,連3D加速的桌面環境都能順暢運作。
但是最近在VMWare中安裝Debian 7 (wheezy)時卻碰到Gnome 3桌面環境不能啟用的問題,原因是在VMWare中,Debian預帶的video driver不能提供Gnome 3需要的3D加速功能。
花了一些時間拜求Google大神,終於在這個blog文章的下方推文找到了最簡單的解決方法。
在這邊簡單敘述解決步驟。

打開任何你習慣使用的terminal,並依序執行下列指令:
sudo apt-get install libxatracker-dev
sudo apt-get build-dep xserver-xorg-video-vmware

apt-get source xserver-xorg-video-vmware -b
sudo dpkg -i xserver*.deb
rm -rf xserver*

重開機後就可以享受Gnome 3的華麗UI介面了!

2014-07-28

[工具]在泛Debian系的Linux中更新certificate檔

沒想到Linux的方便性也是在日益進化的......

如果要在泛Debian系(例如Debian,Ubuntu)的Linux中更新certificate檔,其實很簡單。只要我們執行一下update-ca-certificates這個工具,它就能幫你搞定所有細節了。

許細做法如下:
先取得root權限,把領到的*.crt檔丟到
/usr/local/share/ca-certificates
再用root權限執行
update-ca-certificates
之後連到那些需要用指定的certificate檔才能連立SSL連線的server時,就不會再跳出錯誤訊息了。

2014-04-09

[程式]以tail-recursion來實作已有iterative求解法的recursive數學函數

最近和同好討論到程式方面的問題。對於一個缺乏iterative loop機制的函數式程式語言,要如何做到類似for loop的計算動作呢?最簡單的方法是,在這個函數本體中放入每回iteration時要做的計算動作,在計算動作完成後,呼叫函數自身,並且把要傳到下一輪iteration的數值做為呼叫函數所需的參數。只要這個函數的寫法符合tail-recursive規範,執行於任何有實作tail-recursive最佳化的函數式程式語言(如Scheme、Erlang等等),就不會發生因recursion次數太多造成stack overflow的問題。

當我們要用C語言這類循序式程式語言計算Fibonacci數列中第n個數值是多少時,在n>1的前提下可以用
f(n)<=f(n-1)+f(n-2)
以recursive求解。但是當n很大時,會發生stack overflow的問題,再加上每回求解時,f(n-1)自身也需要用到f(n-2)的結果,使得重複計算的問題持續發生。 既然如此,用iterative的實作方法既能迴避stack overflow,又能重複利用前一輪的計算結果,根本是兩全齊美!但要以缺乏iterative loop機制的函數式程式語言,來實作iterative版的Fibonacci數列求解時,我們就得用點小技巧了。 像是以Lisp-1家族的Scheme來寫Fibonacci函數時,在tail-recursion這部分,除了原有的參數n之外,還要把中間結果a與b做為呼叫函數所需的參數,傳到下一輪iteration:
(define (fibonacci n)
    (define (fibonacci_internal n a b)
        (if (> n 0)
            (fibonacci_internal (- n 1) b (+ a b))
            a))
    (fibonacci_internal n 0 1))
把這段Scheme程式碼拿到支援大數計算的直譯器跑(例如較新版的GNU Guile),就會發現,不論n有多大,這個Fibonacci函數都不會因為stack overflow而當機,也不會因為integer的數值overflow而出現預期外的結果。唯一麻煩的地方,是需要另外定義外部函數fibonacci來包裝函數的參數介面,讓呼叫者不用直接面對需要傳遞a與b的核心函數fibonacci_internal,並在開始執行時自動給a與b一個起始值。

若要循序列舉上述Fibonacci函數的return值,我們也可以用tail-recursive的寫法來循序列出Fibonacci函數求解結果:
(define (loop_test n)
    (define (iter i n)
        (format #t "fibonacci(~a)=~a" i (fibonacci i))
        (newline)
        (if (< i n)
            (iter (+ i 1) n)))
    (iter 0 n))