overtone, emacs ciderの起動
前回、shadertoneをpullしたディレクトリに移動しemacsを起動します。
1 2 3 |
# ~/projects/clojure/shadertoneにgit pullしたことを想定 $ cd ~/projects/clojure/shadertone $ emacs |
emacsにてM-x cider-jack-inを実行すると、
自動でclojure repl, overtoneが起動します。
overtoneを利用するにあたってのclojure基礎
これからコードのサンプルを記載していきますが、
それにあたってclojureの基礎をざっくりと紹介します。
既に基礎を押さえている方は次章まで読み飛ばして下さい。
グローバル変数
defといえば、ruby,python,scalaではメソッド宣言ですが、
clojureではその名前空間でグローバルな変数を宣言します。
1 2 3 4 5 |
(def a 10) (+ a 1) ;=> 11 (* a 2) ;=> 20 (def b (str (+ 10 20))) b ;=> "30" |
ローカル変数
ローカル変数はletマクロで定義します。
1 2 3 4 5 6 |
(def x "xxxx") (let [x 10 y 20] (+ x y)) ;=> 30 x ;=> "xxxx" |
逐次実行
ライブコーディングではリアルタイムにS式を評価していくので
コードブロックを一斉に実行するdoマクロは重宝します。
1 2 3 4 5 |
(do (def x 10) (def y 20) (def z 30) (+ x y z)) ;=> 60 |
メソッド
メソッド宣言はdefnというマクロを利用します。
ブラケットの中には引数が格納されます。
1 2 3 |
(defn foo [n] (+ n 10)) (foo 1) ;=> 11 |
配列(リスト、ベクター)
シーケンス型のオブジェクトとしてはベクター、リストの2種類があります。
どちらもdoseqやmapなどのイテレータメソッドで同様に扱うので今回のライブコーディングで
意識する必要はありません。
ベクターはランダムアクセスに強く、リストは要素の追加に強い特性があることから
ライブコーディングではベクターを多用することになります。
1 2 3 4 5 6 |
(def my-vector [1 2 3 4 5]) (def my-list '(1 2 3 4 5)) (doseq [i my-vector] (prn i)) (doseq [i my-list] (prn i)) |
キーワード
rubyでいうところのシンボルです。
主にハッシュマップなどのキーとして利用することが多いです。
1 2 |
(def h {:a 1, :b 2}) (:a h) ;=> 1 |
スレッディングマクロ(->)
メソッドチェーン風に上から下に処理を記述できるマクロです。
->マクロは関数の第1引数に処理が渡されます。
1 2 3 4 5 6 |
(-> 10 (+ 1) (* 2)) ;=> 22 (* 2 (+ 1 10)) ;=> 22(上と同じ) |
->>マクロは関数の第2引数の処理が渡されます。
1 2 3 4 5 |
(->> (range 3) (map (fn [x] (* 2 x)))) ;=> (0 2 4) (map (fn [x] (* 2 x)) (range 3)) ;=> (0 2 4) |
名前空間の定義、ライブラリのインポート
fooという名前空間を定義しovertone.liveをoという名前でインポートする
1 2 |
(ns foo [:use [overtone.live :as o])) |
サンプル用のファイルを作成
emacsにてサンプル用のファイルを作成します。
ファイル名はまぁ何でもよいので、今回はfoo.cljとします。
foo.cljに以下のコードを記入します。
1 2 3 4 5 6 |
(ns foo (:use [overtone.live :as o])) (definst foo [] (o/saw 220)) (foo) (kill foo) |
名前空間を定義し必要なライブラリをインポートしています。
今回はovertone.liveのみを利用するのでovertone.liveをoという名前でuseしています。
名前空間名は何でもよいのですが、fooとしています。
1 2 |
(ns foo (:use [overtone.live :as o])) |
definstマクロで音を鳴らすためのフォームを定義します。
(o/saw 220)としているので、220Hzのノコギリ波となります。
definstはsuper colliderのsynthdefの形式にコンパイルし呼び出し可能な
関数を返却するマクロです。
ですので(foo)というフォームにてfoo関数を呼び出すと音が鳴る。という仕組みです。
尚、(kill “オブジェクト名”)で音を停止します。
1 2 3 |
(definst foo [] (o/saw 220)) (foo) (kill foo) |
音を鳴らしてみる
emacsにて各式の末尾に移動し、上から順番にC-c C-e(cider-eval-last-sexp)していきます。
(foo)を評価した際に音が鳴るはずです。
最大音量にて再生されるので注意してください。
(kill foo)にて停止します。
(stop)にて現在再生されている全ての音を停止します。
1 2 3 4 5 6 7 |
(ns foo (:use [overtone.live :as o])) (definst foo [] (o/saw 220)) (foo) (kill foo) (stop) |
音源を作成してみる
440hz, ボリューム30%
1 2 3 4 5 |
(definst saw-wave1 [freq 440 vol 0.3] (-> freq (o/saw) (* vol))) |
saw-wave1にゆらぎを加えたもの
1 2 3 4 5 6 7 |
(definst saw-wave2 [freq 440 depth 10 rate 6 vol 0.3] (-> (o/sin-osc:kr rate) (* depth) (+ freq) (saw) (* vol))) |
saw-wave2に音の長さが3秒
1 2 3 4 5 6 7 8 |
(definst saw-wave3 [freq 440 depth 10 rate 6 length 3 vol 0.3] (-> (o/sin-osc:kr rate) (* depth) (+ freq) (saw) (* (o/line:kr 0 1 length FREE)) (* vol))) |
ピアノっぽく叩いた感じ
1 2 3 4 5 |
(definst saw-wave [freq 440 attack 0.01 sustain 0.4 release 0.1 vol 0.4] (-> (o/env-lin attack sustain release) (o/env-gen 1 1 0 1 FREE) (* (o/saw freq)))) |
コードを作成
先ほど作成したsaw-waveに音階を付けてみます。
midi-hz, note関数を利用して音階を設定する関数を作成します。
この関数で周波数ではなく、コードにて音を表現することができるようになりました。
1 2 3 4 |
(defn saw-note [music-note] (saw-wave (o/midi->hz (o/note music-note)))) (saw-note :A4) |
続いてC4のメジャーコードで再生する関数を作成します。
1 2 3 4 |
(defn play-chord [a-chord] (doseq [note a-chord] (saw-note note))) (play-chord (o/chord :C4 :major)) |
時間差で関数を実行する
at関数で時間差実行が可能です。
1 2 3 4 5 6 7 8 |
(defn chord-progression-time [] (let [time (now)] (at time (play-chord (chord :C4 :major))) (at (+ 2000 time) (play-chord (chord :G3 :major))) (at (+ 3000 time) (play-chord (chord :F3 :sus4))) (at (+ 4300 time) (play-chord (chord :F3 :major))) (at (+ 5000 time) (play-chord (chord :G3 :major))))) (chord-progression-time) |
metronomeを利用する
先程の時間差実行でライブコーディングすることも可能ですが、
metronomeを利用すればもっと楽に演奏コードを実装することが容易になります。
秒単位ではなく4分音符単位での表現が可能になります。
1 2 3 4 5 6 7 8 9 10 11 |
(defonce metro (o/metronome 120)) (defn chord-progression-beat [m beat-num] (o/at (m (+ 0 beat-num)) (play-chord (chord :C4 :major))) (o/at (m (+ 4 beat-num)) (play-chord (chord :G3 :major))) (o/at (m (+ 8 beat-num)) (play-chord (chord :A3 :minor))) (o/at (m (+ 12 beat-num)) (play-chord (chord :F3 :major))) (o/apply-at (m (+ 16 beat-num)) chord-progression-beat-loop m (+ 16 beat-num) [])) (chord-progression-beat metro (metro)) (stop) |
metronomeを利用して何が嬉しいかというと、以下のように動的にビートを
変化させることができるからです。
ライブコーディングでは欠かせない機能になります。
1 2 |
(metro-bpm metro 180) (metro-bpm metro 120) |
より汎用的に
sawやsinでも音源を作成することはできますが、
wavファイルなどの音源も利用することができます。
freesound-path関数でfreesoundというサイトから指定のidの音源を
ダウンロードし、バッファに格納します。
再生するにはdefinstと同様、(snare)のように呼びだして再生します。
1 2 3 4 5 |
(do (def snare (sample (freesound-path 26903))) (def kick (sample (freesound-path 2086))) (def close-hihat (sample (freesound-path 802))) (def open-hihat (sample (freesound-path 26657)))) |
player関数を定義しビートを作成しループさせると以下のようになります。
1 2 3 4 5 6 7 8 |
(def metro (metronome 128)) (defn player [beat] (at (metro beat) (kick)) (at (metro (+ 0.5 beat)) (close-hihat)) (at (metro (+ 3.5 beat)) (open-hihat)) (apply-by (metro (inc beat)) #'player (inc beat) [])) (player (metro)) (stop) |
その先へ
上記のようにビートを作成し、再帰でループさせるのもアリですが、
次回はleipzigを利用し、より簡易にループを生成できる方法を紹介します。