max8 max/msp 入門 3
fendoapです。 max/mspについて入門的な記事を箇条書き的にリストアップして書いていこうと思います。
動画です。
gumroadでMax for liveをリリースしています。
シグナルベクターサイズ サンプリングレート
maxで簡単なタイマーのパッチを作るところから話を始めたいと思います。
まずmspオブジェクトを使って簡単な時間をカウントするパッチを作ります。時間は一定の値が増えていくことで確認できます。
例えば1秒間に値が1ずつ増えていくと秒を測ることが出来ます。+=~オブジェクトは値をどんどん足していくオブジェクトです。
これを使って1秒間に値が1ずつ増えるようにするためにサンプリングレートというのを考えます。
サンプリングレートとは1秒間にどれくらいの粒で音をとらえるのかを表したものです。例えば48000Hzなら1秒間を48000個の粒でとらえるという風な感じです。
1/48000ずつ値を増やしていくと1秒後には48000/48000=1となりちょうど一秒間に値が1ずつ増えるようになります。
この+=~ を別のオブジェクトで入れ替えてみようと思います。
+=~とは現在の値に値を足して新しい値とするという操作です。
現在の値を保持して値と足せば同じことが出来そうです。
maxでは無限ループになってしまうのでアウトレットとインレットをつなぐとエラーが起きます。
そこでtapin~ tapout~を使って信号をループしてつないでみます。しかし結果を見ると思ったように値が増えていきません。なぜでしょう。
dawやmaxでは信号はある一定時間間隔で区切りながら計算されます。これをシグナルベクターサイズ、バッファサイズと呼びます。
tapin~ tapoutも同じように一定間隔で区切りながら処理されます。なのでtapout~を0にした場合でもバッファサイズ分のディレイが発生します。これを確認してみます。
信号が何サンプル遅れているか確認するためにノコギリ波のphasor~を使います。phasor~を1Hzにしてサンプルレート倍したものを使うと信号が何サンプル遅れているか確かめることが出来ます。
phasor 1Hzを48000倍したものを測りたい部分に入力します。出てきた信号とそのままの信号を引き算します。
信号が1サンプルずれると値が1サンプルずれるので引き算すればその差がそのまま何サンプルずれているかという値になります。
これは今256サンプルずれているという事を示しています。これはシグナルベクターサイズと同じ値です。つまりtapout~が0の場合でもシグナルベクターサイズ分遅延があるという事です。
試しにdelay~というオブジェクトを使って256サンプル分信号を遅らせてみると差が0になります。
つまり信号が256サンプル=シグナルベクターサイズ分遅れていたという事が分かります。
このパッチに戻るとなぜ表示がゆっくりになるのかが分かります。これはフィードバックディレイのようになってしまっているからです。
本当は1サンプル分ずつ値が増えてほしいのにシグナルベクターサンプル=256ずつになっているのでゆっくりになっています。
これを修正するためには値の増え方をシグナルベクターに合わせた増え方にする必要があります。例えば1サンプルで1増える場合、256サンプルでは値は256増えます。
つまり値をシグナルベクターサイズ分掛ける必要があります。シグナルベクターサイズはadstatusというオブジェクトで取得できますので取得した値を用いて、シグナルベクターサイズ倍にします。
これでうまくいくかどうか確かめます。clockerというオブジェクトは時間を測ることが出来ます。ckockerはmsなので/1000すると秒になります。これを使ってうまくいっているか値を比較すると同じような値になりました。
これは常にそういう遅延があるという事ではなく最小ディレイタイムはシグナルベクターサイズであるという事を意味しています。
例えば10ms=480sampleにしてみるとちゃんと480サンプルディレイされていることが分かります。
サンプリングレート、シグナルベクターサイズによる違いはこのような場合にあります。
phasor ノコギリ波
先ほど時間は一定の値が増えていくとという話をしました。一定の値が増えていく波形にノコギリ波というものがあります。
一定の値で増えていくものをグラフにすると直線になります。時計のようにあるところからまたリセットして0から数えるようにすると波形はのこぎりのような形になります。
のこぎり波を作ってタイマーを作ってみます。ここで1時間を一番大きな区切りとします。周期を遅くするより早くすることが簡単なので一番遅い周期を選択します。
周波数というものを用います。周波数とは繰り返される回数の事です。周波数をヘルツで表します。1ヘルツは1秒間に1回繰り返すことと定義されています。
$$
Hz = \frac{1}{\text{秒}}
$$
ここで1時間は3600秒です。3600秒で1回なのでヘルツは
$$
Hz = \frac{1}{\text{3600}}
$$
となります。
のこぎり波を使って以下のようなパッチを作成します。
@phaseoffset 0. の部分はアトリビュートと言います。さまざまな設定を行うときに書きます。これは初期位相を決定しています。resetというメッセージを送ると0から波形がスタートします。
phasor~はのこぎり波を出力するオブジェクトです。今1時間に1回波形が繰り返すように設定されています。なので3600倍すると値は秒になります。
0~3600まで1時間かけて繰り返します。resetを送ると0からスタートします。
今、値は0~3600までカウントしていきます。これを0~1の繰り返しにするにはどうすればいいでしょうか。ここで使うのがmodulo~です。
modulo~は入力を特定の値で割った余りを出力します。これを剰余演算と言います。
https://ja.wikipedia.org/wiki/%E5%89%B0%E4%BD%99%E6%BC%94%E7%AE%97
例えばmodulo~ 1 であれば1で割った余りになります。これを使うと0~1の値の繰り返しを作ることが出来ます。そしてそれは再びノコギリ波となります。
## 少し脱線してダイアルを使って簡単な描画を作るとこんな感じのタイマーが出来ました。##
JSUIなどを使う場合もありますが、標準オブジェクトの組み合わせでも描画は作れたりします。
bangの取り出し
話をもどしてここからbangを取り出すことを考えます。のこぎり波が0になる瞬間を検知します。このためにdelta~というオブジェクトを使います。
のこぎり波が1→0になる瞬間は他の場合と違い大きな落差があります。
delta~は前の入力との変化を出力するオブジェクトです。のこぎり波が1→0になる瞬間は変化が大きく-1となるのでその部分がとげのようになります。
この部分を検出するため<~オブジェクトを使います。<~ -0.5は条件に適合するときは1それ以外は0を出力します。この場合とげになった時に1それ以外は0となります。
次にedge~というオブジェクトで信号とbangの変換を行います。edge~は0→0以外 0以外→0の時を検出してbangを出力します。この場合edge~の左0→0以外の場合を使います。
ほぼ同じなのでどちらを使っても良いですが右の場合は1サンプル遅れます。 これを用いるとのこぎり波が0になった時にbangが出力されます。
BPMに対応させる
このパッチをよく見ると3600で割って3600を掛けているのでこの部分は省略しても結果は同じになります。
そこで一旦この部分を省略して整理します。
ここでbangをオンオフするためにトグルを追加しました。
またt bというのはトリガーbangの略でトグルを操作するたびにphasor~がリセットされるようになります。
このパッチではbangは周波数に対応しています。1Hzなら1秒間に1回bangされ2Hzなら2回、3Hzなら3回bangされます。
このbangをBPMに応じたタイミングで出力するように変更します。
BPMは BPM = Beat per minute で1分当たりの4分音符の数を表します。
1分当たりなので60で割ると1秒あたりになります。
1秒当たり何回というのは周波数と同じなのでBPM/60すれば周波数になります。このような感じです。例えばBPM:120の時 周波数は2Hzとなります。
これでBPMに応じてbangを出力します。
Max for Live
max/mspによる制作
pure dataによる制作