顯示具有 programming 標籤的文章。 顯示所有文章
顯示具有 programming 標籤的文章。 顯示所有文章

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-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))

2011-04-07

[Tip]const-correctness(常數正確性)

因為以前筆者寫C/C++程式時,中了const的應用陷阱太多次了,所以特地整理了這篇,幫助廣大的華人C/C++初學者了解const的行為。

首先,我們來看,當const關鍵字與pointer、reference變數的宣告一起使用時,會出現什麼情形:
int* ptr; // pointer本身與其指向的記憶區塊皆可覆寫。
int const* ptr; // pointer本身可覆寫,但指向的記憶區塊不可覆寫。
const int* ptr; // 同上。此為常見的寫法。
int*const ptr; // pointer本身不可覆寫,但指向的記憶區塊可覆寫。
int const*const ptr; // pointer本身與其指向的記憶區塊皆不可覆寫。
int const& ref; // reference指向的記憶區塊不可覆寫。附帶一提,reference本身一定不可覆寫。
int&const ref; // reference本身一定不可覆寫,所以沒有這種寫法。

看到這邊大家應該就了解了。在"*"的右邊有沒有const,決定這個pointer本身是不是唯讀;而"*"或"&"的左邊有沒有const,則決定這個pointer或reference所指向的記憶區塊是不是const。

2010-11-10

[PyQt] Warning: correctly use openPersistentEditor() and closePersistentEditor() with entered() signal to keep editor opened

Sometimes we may use openPersistentEditor() and closePersistentEditor() to keep editor of delegate opened.
For example, when entered() signal is generated, we close old persistent editor and open new persistent editor to make real editor widget always locates on the cell that mouse is pointing on.

However, the QModelIndex instance from entered() signal will be destroyed after the slot connected to entered() is returned. If we call openPersistentEditor() with the QModelIndex instance from entered() signal, I found that call closePersistentEditor() with QModelIndex:
1. has same instance as what we used to call openPersistentEditor()
2. has another instance that contains same row, column, and parent as what we used to call openPersistentEditor()
will failed, both of them.

If we want to open persistent editor and close it later in slots that entered() signal is connecting, we should create new index with model.index(index.row(), index.column()).
(Caution: directly assign index.parent() to the 3rd argument of model.index() causes the same problem, parent index should be cloned, too.)

2010-08-17

[Tip]escape shell metacharacters

There is an efficient method to make arbitrary string to escape shell metacharacters while using single-quote('). That is, just suspend a string by ('), add(\'), then continue the string by (').

For example, in Python:
def escapeShellArg(string):
    return u"'"+string.replace(u"'",u"'\\''")+u"'"
This is because UNIX shell explain
testcmd 'test'\''string'
as
testcmd "test'string"
Besides, program testcmd will receive argv=["testcmd","test'string"]. Thus, we could efficiently escape shell metacharacters without regular expression library or other complex string processing library.

2010-06-09

程式開發工具隨意簡介()->1.

現在CPU因為時脈還有加速機制的設計碰到發展瓶頸,單Core性能提身不同於十年前一日千里的態勢,但是依然在逐漸縮小的製程,卻拓寬了處理器單晶片多核心的發展路線,使得今日多核心CPU隨處可見。所以即使是普羅大眾在用的程式工具,都得充份運用多顆CPU來提升程式效率。然而在大多數使用者享受程式的便利性時,又有多少人知道,要做個能夠充份運用多核心,甚至是多台電腦運算資源的程式有多困難。畢竟多個程序同時在不同CPU執行時,中間的溝通往往是少不了的,要找到能讓多個同時執行的程序之間不用互相交換資訊的場合,幾乎可以說是沒有,如何設計出一個程序間能高效率溝通的程式,就成為十分重要的課題。