以前,Clojureで音楽組織プログラミングについて書いた。
それを今度はHaskellで書こうと思う。
Haskellによる音楽構造プログラミングについて。
プログラマが音楽することについて
音楽家でない人が新しい音楽を作ることだってある
現代音楽では有名なヤニス クセナキスという人がいました。彼は音楽家を目指す以前は、建築家であり数学に堪能な人でした。そして、音楽家を目指し、そして偉大な現代音楽家オリヴィエ・メシアンに、師事し、尋ねました。
音楽家になるために、私は音楽の理論を学びたい。何を学ぶべきでしょうか?
そして、メシアンは言いました。
君は数学を知っている。なぜそれを作曲に応用しないのか。伝統的な修練は、あってもなくても同じではないか
そして、クセナキスは気づきました。そして、音楽の構築に彼の持つ数学的な知識、建築の感覚を音楽に応用して、新しい音楽を作曲していきます。大変すごい言葉だと思います。そして、それはプログラマに対しても使えると僕は思います。メシアンの先の言葉を借りると、
君はプログラミングを知っている。なぜそれを作曲に応用しないのか。伝統的な修練は、あってもなくても同じではないか
という訳です。
さて、実際にクセナキスは音楽の構築するために、彼独自の 楽譜 を作成し構築していました。
これがその楽譜です。
非常に数学的、建築的な楽譜だと思います。クセナキスが独自に楽譜を作り音楽を構造にし、これを実際には、五線楽譜になり、演奏家に演奏をさせていました。
さて、彼がこのように楽譜を書いて作曲していたのであれば、ではプログラマの楽譜とはどのようになるのでしょうか?これが今回の本題です。そしてそれは僕は、「プログラミングのコード」そのものであると思います。
##Super Colliderについてちょっとだけ
さて、今回、Haskell + SuperColliderで音楽をプログラミングしていくわけですが、簡単にSuperColliderについて紹介して、おきます。
SuperColliderは、音楽プログラミング言語であり環境です。Super Colliderをは、内部クライアントとサーバを持っています。クライアントでプログラミングして言語を解釈し、それをScsynthサーバに投げ、サーバが音楽を鳴らすというわけです。アーキテクチャは以下のような感じです。
hsc3について
hsc3は、そのSuperColliderクライアントです。
インストールとか
インストールは
cabal install hsc3
です。んで、なんか、emacs-modeが用意されているので、.emacsやら、.emacs.d/init.elやらに、設定を追加します。
(add-to-list 'load-path ".cabal/hsc/yourpath/emacs")
(setq hsc3-help-directory ".cabal/hsc/yourpath/Help/")
(require 'hsc3)
パスは適当なものを設定しておいてください。
SCサーバを立ちあげる。
インストールされた、scsynthでやる。Mac だとこんな感じ。
/Applications/SuperCollider/SuperCollider.app/Contents/Resources/scsynth -u 57110
てきとーになんか鳴らす
まず、emacsを立ちあげ、M-x hsc3-mode にします。そして、C-c > (>も入力してね。)すると、Replが立ち上がります。
んで、
import Sound.SC3
withSC3 (send (g_new [(1, AddToTail, 0)]))
audition (out 0 (sinOsc AR 440 0 * 0.1))
で,440HzのSin波が出力されます。んで、止めるときは
withSC3 reset
で、とりあえず止まります。
Haskellによる音楽プログラミング
さて、Super Colliderについてはいろいろググって参照してもらうとして、
ここでは、Haskellでどう音楽を作っていくかについて、方法を述べていきたいと思います。
考えかたについて
僕は音楽プログラミングをするとき、大きく分けて2つのデータを記述することを考えています。
- 楽譜的データ
- 演奏情報的データ
楽譜的なデータとは、みなさんが思い浮ぶような五線楽譜のようなものではなく、むしろ、図形楽譜に近い考えをしています。どういうことかといえば、自由に解釈され演奏されうる、楽曲構造を表わしているということです。
そして、演奏情報データとは、その楽譜的なデータを解釈して、実際に音を作るデータのことです。
楽譜的データについて
以前、Clojureで音楽組織プログラミングについて書いたのは、基本的に楽譜的なデータをプログラミングで記述すべきということを書きました。
前回はリストで表わしていましたが、 楽譜的なデータは別にそれはリストじゃなくても良くなんでもいい のです。どのように解釈しても良いわけですし、演奏情報に解釈できれば良いのですから。ただリストは扱いやすかったから採用しただけです。
ちなみに、今回の楽譜的データというのはどのように時間的に音を配置するかという 時間的構造 として作成します。どういうことかといえば、楽譜的データの記述は実は 時間的構造以外にもありうる ということも念頭に置いてください。
さて何かモチーフとなるデータを作っておきます。
sample = [1,1,3,3]
さてこれをどんどん好きに組みあわせて行きましょう
sample2 = sample ++ sample ++ reverse sample ++ [0,1,0]
sample3 = map (+2) sample2
好きに構造を大きくしても構いません。モチーフをつくるセンスは問われますが。自分は普段は2つの対比的データをつくり(たとえば変化が大きい、小さいなど)、組みあわせます。
ここで、どう構造化するか?について理論的なものを考えてみたいですけどね。
演奏情報のデータをつくる
さて、ここでは、 楽譜的なデータがInt型のリストなので、リストの要素を取りInt型に出力音を対応づけます。
performList list period inst =
let
interval = Constant_U $ Constant $ 1 / realToFrac (length list)
freq = Constant_U $ Constant $ realToFrac (1/period)
z = impulse AR freq 0
in
foldr1 (+) $ map (\ (i,x) ->
(toggleFF ((tDelay z $ i * interval * period ) +
(tDelay z $ (i + 0.5) * interval * period ))) *
(inst x)) $ zip [0..] list
さて、ここで, listと、period, instのデータがありますが、 完全には演奏情報データは作らない 良いと思います。なんでかっていると、作曲中、楽曲の時間構造などもまだつくられてなく、むしろ、楽曲構造と演奏情報を互いに補いつつ楽曲を完成させていく のが大事になってきます。
とは言うものの、 実際に音を出さないとつまらないので、音を出しましょう。(というより、実際にはもっと早くから音を聞いてます。)
とりあえず音をならす
とりあえず以下のコードとで音を出しました。
phrase2 = performList sample3 5 (\ x -> saw AR (x * 200))
audition (out 0 phrase2)
音を実際に鳴らした時のやつ。
http://www.youtube.com/embed/xqdnujRfFrg
楽譜的データと演奏情報の混在するデータを更に組み合せる
基本的にフレームワークやデータ構造によりますが、さらに組みあわせることによって、音楽に構造をもっと持たせるようにします。
ただしこの時、以前決めたことや、フレームワークに依る制約によってどんどん、自由度が少なくなっていきます。
sawPhrase1 = performList sample3 10 (\ x -> saw AR (x * 200))
sawPhrase2 = performList sample3 5 (\ x -> saw AR (x * 200))
sawPhrase3 = performList sample3 1 (\ x -> saw AR (x * 200))
sawPhrase = sawPhrase1 + sawPhrase2 + sawPhrase3
sinPhrase1 = performList sample3 10 (\ x -> sinOsc AR (x * 200) 0)
sinPhrase2 = performList sample3 5 (\ x -> sinOsc AR (x * 200) 0)
sinPhrase3 = performList sample3 1 (\ x -> sinOsc AR (x * 200) 0)
sinPhrase = sinPhrase1 + sinPhrase2 + sinPhrase3
音を鳴らす
さて、また音を鳴らしてみましょう。
audition (out 0 sawPhrase4)
audition (out 0 sinPhrase4)
あと、適当に制作風景を流しまう。
http://www.youtube.com/embed/33om5LlFbEg
ブログ内で完成させる予定だったけど。
曲完成できなかった
最後にまとめ
最後にいろいろしましたが、
- プログラミングの音楽についての僕なりの考え
- Haskell + SuperCollider
- 演奏情報と時間的音楽構造によるプログラミング
について述べさせて頂きました。
曲完成できなかった。
言いわすれてたけど、曲を一曲完成させるつもりでした。
敗因 :: 軽くなんかつくろうとおもったら、いろいろ凝りすぎた。凝り性つらい。
あと書きたかったこと
モナドを使った音楽を書こうとしたけど、それには僕の時間が余りにも足りない!!!
実はもっと色々書きたかったけど、結局浅い内容になってしまいました。
実際に、Haskellらしい構文、表現における音楽性についてもっと書きたかった.....
おまけ
紹介しようと思って、かいたけど、結局つかわなかったコード群
- なんかノイズ音楽作ろうと思ったけど、一般向けにはコア過ぎてアレだったやつ。
performNoize period =
let
freq = Constant_U $ Constant $ realToFrac (1/period)
z = impulse AR freq 0
z' = tDelay z (0.5 * period)
o = whiteNoise 'a' AR * 0.8
in
mce [z * 0.1,toggleFF z' * o]
- なんかワンライナーで強引に書いたやつ。セミコロンあるからアレ。しかも曲になっているというw...
let {compose = foldl (flip (.)) id; rvb z s = let f i = let dly = mce [rand (z `joinID` i `joinID` 'a') 0 0.5 ,rand (z `joinID` i `joinID` 'b') 0 0.5] in allpassN i 0.05 dly (rand i 1.5 2) in compose (replicate 5 f) s; stpr = let {rate = mouseX KR 1.75 2.25 Exponential 0.1; clock = impulse KR rate 0; envl = decay2 clock 0.002 2.5; indx = stepper clock 0 0 15 1 0; freq = bufRdN 1 KR 10 indx Loop; ffreq = lag2 freq 0.1 + mce [0,0.3]; lfo = sinOsc KR 0.2 (mce [0,pi/2]) * 0.0024 + 0.0025; top = mix (lfPulse AR (freq * mce [1,1.5,2]) 0 0.3); chn = [\s -> rlpf s ffreq 0.3 * envl ,\s -> rlpf s ffreq 0.3 * envl,\s -> s * 0.5 ,\s -> combL s 1 (0.66 / rate) 2 * 0.8 + s ,\s -> s + (rvb 'a' s * 0.3) ,\s -> leakDC s 0.1 ,\s -> delayL s 0.1 lfo + s ,\s -> onePole s 0.9]} in compose chn top; stprInit = let n = [97.999,195.998,523.251,466.164,195.998,233.082,87.307,391.995,87.307,261.626,195.998,77.782,233.082,195.998,97.999,155.563] in do {_ <- async (b_alloc 10 128 1) ;send (b_setn 10 [(0,n)])}}in withSC3 (stprInit >> play (out 0 stpr))