LoginSignup
71
68

More than 5 years have passed since last update.

Haskellでの 音楽表現

Last updated at Posted at 2013-12-12

以前,Clojureで音楽組織プログラミングについて書いた。

それを今度はHaskellで書こうと思う。

Haskellによる音楽構造プログラミングについて。

プログラマが音楽することについて

音楽家でない人が新しい音楽を作ることだってある

現代音楽では有名なヤニス クセナキスという人がいました。彼は音楽家を目指す以前は、建築家であり数学に堪能な人でした。そして、音楽家を目指し、そして偉大な現代音楽家オリヴィエ・メシアンに、師事し、尋ねました。

音楽家になるために、私は音楽の理論を学びたい。何を学ぶべきでしょうか?

そして、メシアンは言いました。

君は数学を知っている。なぜそれを作曲に応用しないのか。伝統的な修練は、あってもなくても同じではないか

そして、クセナキスは気づきました。そして、音楽の構築に彼の持つ数学的な知識、建築の感覚を音楽に応用して、新しい音楽を作曲していきます。大変すごい言葉だと思います。そして、それはプログラマに対しても使えると僕は思います。メシアンの先の言葉を借りると、

君はプログラミングを知っている。なぜそれを作曲に応用しないのか。伝統的な修練は、あってもなくても同じではないか

という訳です。

さて、実際にクセナキスは音楽の構築するために、彼独自の 楽譜 を作成し構築していました。
これがその楽譜です。

img_1426615_47725713_2.jpeg

fig_1.gif

非常に数学的、建築的な楽譜だと思います。クセナキスが独自に楽譜を作り音楽を構造にし、これを実際には、五線楽譜になり、演奏家に演奏をさせていました。

さて、彼がこのように楽譜を書いて作曲していたのであれば、ではプログラマの楽譜とはどのようになるのでしょうか?これが今回の本題です。そしてそれは僕は、「プログラミングのコード」そのものであると思います。

Super Colliderについてちょっとだけ

さて、今回、Haskell + SuperColliderで音楽をプログラミングしていくわけですが、簡単にSuperColliderについて紹介して、おきます。

SuperColliderは、音楽プログラミング言語であり環境です。Super Colliderをは、内部クライアントとサーバを持っています。クライアントでプログラミングして言語を解釈し、それをScsynthサーバに投げ、サーバが音楽を鳴らすというわけです。アーキテクチャは以下のような感じです。

スクリーンショット 2013-12-12 10.33.59.png

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))
71
68
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
71
68