はじめに
TidalCyclesは通常、実行したリズムパターンを永遠に繰り返します。
ただ、指定したリズムパターンを1サイクルだけ実行するonce関数というものがあります。
たとえば次のような簡単なハイハットが鳴るだけのリズムパターンを考えます。
d1 $ s "808oh*4" # speed "2 1 1 1"
このリズムが鳴っているときに、以下を実行すると、bd
(バスドラ)が一度だけ再生されると思います。
once $ s "bd"
しかし、これを実行してから実際にbd
が再生されるまでにはレーテンシーが発生しますし、次のような音数が多いパターンなどになるとサイクルと無関係に再生されてしまうのは不便かと思います。
once $ s "[~bd]*4 ~"
そのため、実行したあと、次のサイクルの頭に再生してくれる機能が欲しくなります。
once'関数の実装
同じこと考えているひとがいないか、TidalのコミュニティTidalClubで探してみたところ、ドンピシャなものが見つかりました。
これによると、以下のようにしてonce'関数を実装してみれば良いらしいです
seqC x y pt= (p x . (|< orbit (x-1))) $ qtrigger x $ seqP [(0, y, pt)]
once' x = seqC x 1
ただ実行してみるとわかる通り、これは以下のエラーが出ます。(動作環境: macOS, TidalCycles 1.7.10)
t>
• Couldn't match expected type ‘Pattern Int’
with actual type ‘Sound.Tidal.ID.ID’
• In the first argument of ‘orbit’, namely ‘(x - 1)’
In the second argument of ‘(|<)’, namely ‘orbit (x - 1)’
In the second argument of ‘(.)’, namely ‘(|< orbit (x - 1))’
これはp x
のxの型がIDであり、他のxはPattern Intとして扱われているために起こるエラーです。残念ながら自分ではIDとPattern Intの変換関数を見つけることができなかったので、あまり使わないd16
チャンネルをonce'関数用のチャンネルとして使うことにしました。
最終的なコードはこちらで、これをBootTidal.hs
に追記すればonce'関数が使えるようになっていると思います。
:{
seqC :: Time -> Pattern ValueMap -> IO ()
seqC y pt= (p 16 . (|< orbit 15)) $ qtrigger 16 $ seqP [(0, y, pt)]
once' :: Pattern ValueMap -> IO ()
once' = seqC 1
:}
BootTidal.hsへの追記方法はこちらが参考になるかと思います
テスト
試しにonce'関数を動かしてみましょう。
d1 $ s "808oh*4" # speed "2 1 1 1"
once' $ s "[~bd]*4 ~" # orbit 1
まずd1の方を動かしてから、once'のほうを動かしてみてください。
once'を実行した、次のサイクルの頭に指定したリズムパターンs "[~bd]*4 ~"
が一度だけ再生されるのを確認できるかと思います。
ちなみに# orbit 1
をつけているのは明示的にどのチャンネルとして音を出したいかを指定するためで、例えば以下のようにd1にリバーブをかけた状態だで# orbit 0
を指定すると、bd
にもリバーブがかかってしまいます。
d1 $ s "808oh*4" # speed "2 1 1 1" # room 0.7
once' $ s "[~bd]*4 ~" # orbit 0
これはd1→orbit 0
, d2→orbit 1
, d3→orbit 2
, ... とデフォルトで各dnに対してorbitが割り振られているためで、once'のorbitを1にしてあげればd1のリバーブが掛からずにすみます。