【TidalCycles】ライブコーディングによる作曲のすすめ
はじめに
TidalCyclesは、Haskellライクなコードによってリズムパターンを生成できるソフトウェアです。
いきなりですが、TidalCyclesでこんな曲つくってます、よければ聴いてください!
最近(2021年6月)公式ページがアップデートされ、以前は詳しい関数などの解説が簡易的なものでしたがだいぶ充実するようになってきました。
MacOSの場合はinstall方法が
curl https://raw.githubusercontent.com/tidalcycles/tidal-bootstrap/master/tidal-bootstrap.command -sSf | sh
を実行するだけ(最新情報はこちら)となり、以前に比べて非常に楽になったので、別の作曲・演奏の道具が欲しい方は試してみてください。WindowsはMacのように1コマンドとはいきませんが、こちらの方法でインストールできます。
インストールできたら公式のチュートリアルを実行しながら読み進めてみるとどんなことができるのかざっくりつかめると思います。
ちなみに自分はHaskell自体を全く書いたことがないのですが、関数の定義方法くらいを調べただけで言語の壁はまだ感じずに済んでいます。
作曲でTidalCyclesを使うメリット
Tidalを始めてから3ヶ月少し経ちますが、いろいろ気づいたところがあったのでまとめます。
1. コードで音を作れる
一番の特徴は、なによりGUIでぽちぽちしながら作るのではなくコードを書きながら音を表現できるところです。
個人的にはGUIを使いこなすことが苦手で、どこにどんな機能があるのか気付けず、頑張って調べてもググってでてきた記事とDAWのversionが違うためそのハードルで作曲どころじゃない、ということがよくありました。(向いてなさそう)
Macの場合は~/Library/Application Support/SuperCollider/downloaded-quarks/Dirt-Samples/直下に音声ファイルの入ったフォルダがあり、このフォルダ名で内部の音声の再生ができます。TidalCyclesでこの音声を呼び出してリズムパターンを作れます。
またリズムパターンだけでなく、SuperColliderで自作シンセをつくることができ、これもコードで定義できるのでいろいろ世界が広がります。(Ableton liveなどの外部のシンセを使うこともできるみたいです)
シンセの定義はyoppaさんの記事ががわかりやすいです。
2. バージョン管理できる
コードでかけることの最大のメリットは、Git管理できることです。
ライブコーディングで用いるスクリプトの場合は、同じ曲でもいろいろ書き換えて演奏するため管理がしづらいかもしれませんが、今回のように「作曲する」という目的のためでしたら後述するseqP, resetCyclesToなどを用いて曲の始めから終わりまで自動再生するということが可能になります。
そのためバージョン管理が容易になるのでソフトウェアエンジニアとしてはとても嬉しいです。ライブコーディング用の.tidalファイルは作曲用の.tidalファイルとは分けて管理するのがいいかなと思います。
3. トラックがすぐに作れる
一番下にも動画とコードを載せていますが、一旦表現したい音をつくる分にはとても速いです。なにより、慣れてくると頭に鳴ったリズムを楽器などがなくても一旦コードでメモしておくとかできるのがいいです。
もちろん演奏したい音楽による向き不向きはあると思いますが、例えばポリリズム的なことやりたかったら
d1 $ s "{808sd:11*3,808oh*4}"
のようにするだけです。
4. 作ってて飽きない
これも僕がDAWで作曲するときに困っていたことではありますが、「昨日途中まで作ったトラックを今日聴いたらなんとなく微妙な感じする」と思って、そのままなにが原因かわからず途中で製作を放棄した曲がたくさんあります。(性格の問題か、自分の作りたい音楽の性質起因かもですが)
TidalCyclesをつかってみてわかったのは、「毎回音に少しランダム性を持たせることができるので、繰り返し聴いても飽きない」ということでした。その証拠に、一つのトラックを細かく区切って録音したものループして聴くとすぐに飽きがきてしまい、Tidalで再度音を聴いてみるとちゃんと飽きずに聴くことができた、という経験をしました。
例えば上で作ったポリリズムも、こんな感じでランダム性を持たせれば、次の音の予測が難しく魅力的になります。
d1
$ every' 8 7 (ply "1 1 1 12" . (|* gain 0.9))
$ sometimesBy 0.4 (jux(rev))
$ sometimesBy 0.1 (density 2)
$ s "{808sd:11*3,808oh*4}"
# pan (range 0.3 0.7 rand)
関数の使い方などのTipsは、moistpeaceさんの記事がとてもわかりやすいです。
TidalCyclesで作曲する場合の弱点
1. 曲の展開が作りづらい
どうしても「リズムパターンを作る→音を重ねていく」という音の作り方がやりやす過ぎるため、1つのリズムパターンやコード進行の中で完結しがちになってしましいます。
もちろんそういう曲もいいですが、3~5分程度の曲をwavファイルなどのファイル形式として作成する場合、TidalCycles得意のランダム性の本領発揮にはなりません(なぜなら一度書き出された曲かには、「ランダム性によって作られた」という文脈だけが残り、ランダム性自体は聴けば聴くほど消えていくからです)
ファイル形式の曲にするのであればやはり展開をつくったり表現したいことにちゃんと向き合うのは必要かなあと個人的にはぼんやり考えています。
そのため、いい感じにトラックができたタイミングでDAWに各チャンネルの音を切りはりしていって、次の展開のコード進行やリズムパターンなどを楽器や打ち込みなどで見通しを立ててからTidalに戻ってくると、アイデアが凝り固まらずに開発できてよさそうです。
展開が決まってきたら、seqPをつかって、「nサイクルからmサイクルまでこのパターンを再生する」というような指定をするのがおすすめです。下で紹介するresetCyclesTo (-0.05)と組み合わせると力を発揮すると思います!
d1 $ seqP [
(0, 4, sound "bd bd*2"),
(4, 8, sound "hh*2 [sn cp] cp future*4"),
(8, 12, sound "arpy*8")
]
たとえば上のようにすれば、
0~3サイクル: "bd bd*2"
4~7サイクル: "hh*2 [sn cp] cp future*4"
8~11サイクル: "arpy*8"
のように指定できます。
2. 意図したタイミングで音が出せない
SuperCollier上でSuperDirtを起動してから、TidalCyclesのサイクル数のカウントが始まります(0サイクルスタート)。2つ上のコードのように
$ every' 8 7 (ply "1 1 1 12" . (|* gain 0.9))
と設定すると、8サイクル周期で7サイクル目のときplyエフェクトがかかるわけですが、「今何サイクル目か?」がわからないと意図通りのタイミングで音が出せないわけです。
また、曲の始まりも意図していない位置から再生される可能性があります。
実を言うと、「今何サイクル目か?」という確認はデフォルトではできません(頑張ればできるので、これは別記事で書きました。)
そのため、「何サイクル目から再生するか指定して再生する」というのが良い立場だと思っています。
TidalCyclesを0サイクル目から再生する方法についても別記事に書いておきました。
例えば、上のseqPの例のコードは
import qualified Sound.Tidal.Tempo as T
resetCyclesTo n = T.changeTempo (sTempoMV tidal) (\t tempo -> tempo {T.atTime = t, T.atCycle = n})
do
resetCyclesTo (-0.05) -- 0サイクル目から再生
d1 $ seqP [
(0, 4, sound "bd bd*2"),
(4, 8, sound "hh*2 [sn cp] cp future*4"),
(8, 12, sound "arpy*8")
]
とすれば0サイクル目から実行できますし、たとえば4サイクル目から再生したい場合は
do
resetCyclesTo (4-0.05) -- 4サイクル目から再生
d1 $ seqP [
(0, 4, sound "bd bd*2"),
(4, 8, sound "hh*2 [sn cp] cp future*4"),
(8, 12, sound "arpy*8")
]
とすれば再生できます。(-0.05はサイクル指定してから再生されるまでの遅延を補正している、という感じです)
3. 録音機能がない
せっかく曲を作ったら、そのまま録音して公開・・といきたいところですが、標準では録音機能が用意されていません。そのためいまのところは内部音声をそのまま録音する、というのが一般的みたいですね(MacOSの場合: TidalCyclesの録音をする Windows10の場合: Windows 10 の Xbox Game Bar の概要)
自分もこのリンクの方法で録音していますが、QuickTimePlayerで録音する際にはオーディオ収録の品質を最高にしておくと音声が圧縮されずに録音されます。
録音した音声ファイルの音量をあげたい場合は、そのままffmpegなどで音量をあげると音が割れることがあるのでLogic ProのAdaptive LimitterでGainをあげるのが良いかなと思います。Logic Proがない方は、お使いのDAWソフトでマキシマイザーの機能があるか調べてみてください。
トラックの例
大好きなrei harakamiのJoyの冒頭部分をTidalCyclesで演奏してみました。シンセはSuperColiderで自作してしまっている(MyMoog, superhammondBend, SinOscSynthBend)ので、適当に置き換えていただけると再生できると思います(バスドラやシンバルなどの音源はデフォルト音源をそのままつかってます)
コードもせっかくなので載せときます(Ctrl+Enterしたら全自動再生されます)
import qualified Sound.Tidal.Tempo as T
resetCyclesTo n = T.changeTempo (sTempoMV tidal) (\t tempo -> tempo {T.atTime = t, T.atCycle = n})
setcps (120/60/4)
do
resetCyclesTo (-0.05)
let
cy1pat = "0 ~ ~ ~ ~ ~ ~ ~ [1 1] 0 0 ~"
cy2pat = "0 ~ 0?? ~ 0? 0? 0 ~ [1 1] 0 0 ~"
bass = "cs3 cs3 d3 d3 e3 e3 fs3 b1"
d1 -- main chord
$ seqP[
(0, 4, (
slow 2 $ s "MyMoog*6 MyMoog*6"
# n "[<af4,e5>] [<a4,e5>]"
)),
(4, 64, (
slow 1 $ s "MyMoog*3 MyMoog*3"
# n "[<af4,e5>] [<a4,e5>]"
))
]
# sustain 1.0
# sustainpedal 0.125
# pitch1 (range 1.8 2.0 (rand))
# shape 0.1
# hpf 60 # hpq 0.2
# room 0.6
# gain 1.0
# pan (range 0.4 0.6 rand)
d2 -- hi-hat snare
$ seqP[
(4, 6, (
s (fit 0 ["hh27:7", "808:4"] cy1pat)
# cut 1 # gain "1.3 ~ 1.3 ~ 1.3 1.3 1.3 ~ [1 1] 1.3 1.3 ~"
)),
(6, 64, (
s (fit 0 ["hh27:7", "808:4"] cy2pat)
# cut 1 # gain "1.3 ~ 1.3 ~ 1.3 1.3 1.3 ~ [1 1] 1.3 1.3 ~"
)),
(7, 64, (
s "~ ~ ~ [~ ~ superhammondBend/4]"
# voice 1 # n "c4" # sustain 0.2 # sustainpedal 0.4 # slide 2 # hpf 60 # hpq 0.3 # gain 1.0
)),
(12, 64, (
fast 4 $ s "[808oh ~ 808oh/2] 808sd:11" # gain "1 1.1" # hpf 60 # hpq 0.2
))
]
d3 -- bass drum1
$ seqP[
(7, 16, (
fast 4
$ every' 3 1 (const (s "~ bd:19"))
$ every' 3 0 (const (s "bd:19 ~"))
$ s "~"
# hpf 1000
# gain 1.13
)),
(16, 64, (
fast 4 $ s "bd:19 ~" # speed "<1 0.8> 1" # hpf 60 # hpq 0.2 # gain 1.13
))
]
d4 -- symbal
$ seqP[
(16, 64, (
stut' 2 (1/4) ((|* gain 0.9).(|> pan (1)))
$ stut' 6 (1/2) ((|* gain 0.9).(|> pan (0)))
$ s "808cy:2/8"
# pan "0"
# gain 1.1
))
]
d5 -- click
$ seqP[
(16, 64, (
fast 4
$ every' 3 0 (const (
s "SinOscSynthBend ~" # n "<af4,af5,af6>" # hpf 700 # lpf 800 # sustain 0.05 # pan 0.3))
$ every' 3 1 (const (
s "~ SinOscSynthBend" # n "<af4,af5,af6>" # hpf 700 # lpf 800 # sustain 0.05 # pan 0.7))
$ s "~"
# gain 0.9
# pan rand
))
]
d6 --bass
$ (|* gain 1.1)
$ seqP[
(18, 20, (
slow 2
$ n bass
# s "superhammondBend"
# gain "0 0 0 0 1 1 1 1"
)),
(20, 64, (
slow 2
$ n bass
# s "superhammondBend"
# gain "1 0 1 0 1 1 1 1"
))
]
# sustain 0.5
# sustain "0.46 0 0.46 0 0.46 0.46 0.46 0.46"
# voice 1
d7
$ seqP[
(24, 64, (
(|* gain 1.1)
$ stut' 2 (1/4) ((|* gain 0.9).(|* lpf 0.8).(|> pan (0)).(|> slide 0))
$ stut' 6 (1/2) ((|* gain 0.9).(|* lpf 0.8).(|> pan (1)).(|> slide 0))
$ every' 4 0 ((|> n "~ [[fs6 af6 e6] b5] ~ ~").(|> gain 1).(|> pan (0.3)))
$ s "~ [superhammondBend*3 superhammondBend] ~ ~"
# sustain 0.1
# room 0.1
# gain 0
# lpf 1000
# voice 0
# pan (slow 3 $ sine)
))
]
まとめ
長々と書いてきましたが、DAWとの最大の(思想の)違いは
DAWが時間軸に音を配置していくのに対し、TidalCyclesは空間に時間軸込みの音を配置していく
ということだと思っています。
そのため作曲者側も音の捉え方が変わるので普段DAWで作曲している方にも、DAWでの作曲がしっくりこない方にも試してもらうと世界が広がるんじゃないかなーと思います。(あと個人的には技術力のあるエンジニアにもっとクリエイティブコーディングの世界に入ってきてもらいたい)
それでは良いTidalライフを!!