Max/MSP使っていると困るのがパラメーターの管理です。とりあえず適当にPresetで保存読み出ししておけばいいけど、テキストでも編集したいとなると古くから使われているcollとかjsonで保存できるdictとか、よくわからないpattrとかいろいろありすぎてわかりません。 今回は一番良くわからなそうなpattrを真面目に使ってみようという話です。
目標
例えば、いろんな自分で作ったモジュールのパラメーターをMIDIコンからコントロールして、かつプリセットにも保存されて、というのがやりたい。 言うのは簡単ですが実際やると、
- パッチ上でマウスで編集した値がコントローラー側に反映されておらず、コントローラーを動かすと値がいきなり飛ぶ
みたいな現象が起きて、これを改善しようと思うとsendとreceiveで双方向に値を送り合ったりしてパッチがグッチャグチャになっていきます。地獄。pattrを使うとこれがうまいこと解決できます。
pattr入門
まずpattrの一番シンプルにわかりやすい挙動について見てみましょう。
分かりますでしょうか。注目すべきはpattrの2番めのアウトレットに繋がれたfloatオブジェクトです。
アウトレットに何も繋がっていないにも関わらず、ここをドラッグすると同じ名前をつけたpattrオブジェクトの左アウトレットにつながっているfloatの値が変更されます。
この双方向バインディングこそがpattrのミソですが、これは上から下にメッセージが流れるデータフローの原則を破っており、わかりにくさの原因でもあります。
そのためこの2番めの"bind"アウトレットはfloatやsliderなど、単一の値を持つ一部のオブジェクトにしか接続できないという特徴があります。umenuのように、インスペクタの中でpattrに保存する値をリストのインデックスの数値にするか、実際の選択肢のシンボルにするかを選べるものもあります。
サブパッチャーにアクセス
面白いのはサブパッチャーの中のパラメーターにもアクセスできることです。例えばディレイタイム、フィードバック、スプレッド、ミックスという4つのパラメーターを持つディレイのサブパッチを作り、中でパラメーターにpattrで名前をつけバインドしておきます。
これを親パッチから2つそれぞれ呼び出して中のパラメーターを個別に操作することが出来ます。今回はfeedbackをコントロールしたい。
ややこしいポイントとしては、サブパッチ、bpatcherにはpattrオブジェクトはバインド出来ません。
[p fuga]
のようなサブパッチなら[bindto fuga::feedback<
、bpatcherならインスペクタを開いてScripting nameを編集し[bindto (scriptingname)::feedback<
というようにするとアクセスできます。
pattrstorageで保存
pattrのもう一つの特徴に、出てきたpattrオブジェクトの値を自動でまとめてくれるpattrstorageというものがあり、json/xmlにエクスポートしたり、presetオブジェクトと連携が出来たりします。
とりあえず作ってみて、[clientwindow<
というメッセージを送り保存される値を見てみましょう。
上2つにu〜〜〜という変なのがいますね。これは名前をつけてないpattrオブジェクトが自動的に検出されているからです。 コントローラー側は特に値を保存しなくてもいいのでこれは邪魔です。 しかしちゃんとそのへんは考えられていて、
@invisivle 1
というアトリビュートをつけてやると解決します。
さて、[pattrstorage store]
と名前をつけたうえで[preset @pattrstorage store]
というpattrstorageと結びつけたpresetオブジェクトを作り、1つ2つ値を保存してみます。
その後[writejson<
というメッセージをpattrstorageに送り、値を保存すると中身はこのようになっております。
//storage.json
{
"pattrstorage" : {
"name" : "store",
"slots" : {
"1" : {
"id" : 1,
"data" : {
"pingpong1::delaytime" : [ 0.0 ],
"pingpong1::feedback" : [ 0.548 ],
"pingpong1::spread" : [ 0.0 ],
"pingpong1::mix" : [ 0.0 ],
"pingpong2::delaytime" : [ 0.0 ],
"pingpong2::feedback" : [ 0.54 ],
"pingpong2::spread" : [ 0.0 ],
"pingpong2::mix" : [ 0.0 ]
}
}
,
"2" : {
"id" : 2,
"data" : {
"pingpong1::delaytime" : [ 0.0 ],
"pingpong1::feedback" : [ 0.7 ],
"pingpong1::spread" : [ 0.0 ],
"pingpong1::mix" : [ 0.0 ],
"pingpong2::delaytime" : [ 0.0 ],
"pingpong2::feedback" : [ 0.16 ],
"pingpong2::spread" : [ 0.0 ],
"pingpong2::mix" : [ 0.0 ]
}
}
}
}
}
割と可読性も高いし、テキストで編集も大変では無さそうですね。
ここで今、全てのパラメーターにpattrオブジェクトをバインドするのは面倒に思えるかもしれません。その場合は、サブパッチャーの中に[autopattr @autoname 1]
というオブジェクトを配置します。そうすると、サブパッチ内に置かれた全ての数値オブジェクトなど、pattrの2番目を繋げられるオブジェクトに仮想的にpattrオブジェクトがバインドされたことになります。この場合のバインドされるパラメーター名は、オブジェクトを選択してインスペクターを開き、“Scripting Name"から設定できます。
注意点としては、デバッグ用などで値の保存が挙動に影響しない数値オブジェクトなどが乱立していると、要らない値まで沢山pattrstorageに保存されて可読性が低くなることがあります。逆に言えばこれを活用するとパッチの中で本当に必要なパラメーターを絞り込むことができるのですが、どうしてもデバッグ用などで数値オブジェクトを残したい場合、先ほど同様pattr @invisible 1
を手動でバインドしてあげる、という手を取ることになります。
別のパッチからコントロールする
さて、実際にMIDIコンなどと連携したければコントローラーと実際の処理は別パッチで出来たほうが使いまわしもしやすいです。 一度jsonファイルを経由すれば、これも出来ます。
実際の処理部分のパッチをmain.maxpat
、コントローラーのパッチをcontroller.maxpat
としておきましょう。
で、controllerの方はいきなりですがこうします。
pattrの手前で[sprintf ::main::%s]
となっていますが、これは名前がmain.maxpatだからこうなります。
これで、ドロップダウンメニューから好きなパラメーターを選択してinletに好きな値を放り込めば完成です。
実際
実際使った時はこんな感じでやってました。
ArturiaのBEATSTEPというMIDIコントローラーと一緒に使う用に作っていて、画面上の配置も物理配置そのまんまにしています。
BEATSTEPのいいところは、ノブがエンドレスロータリーエンコーダーになってて、Relativeモードというのが使えるところです。relativeモードでは回した速度によってインクリメント、デクリメントを送ってくるモードで、最初に挙げたmidiコントローラー側が絶対値を持ってしまいパッチ側で設定した値が飛んでしまうという問題を回避することが出来ます。
通常モード
プレゼンテーションモード
コントローラー側でも、
- どのCCをどのパラメーターを割り当てる
- 絶対/相対モード
- ノートデータの場合、ラッチ/アンラッチ
みたいな切り替えを選択でき、この割り当て情報自体をまたpattrで保存して値のマッピング情報自体をjsonにインポート/エクスポート出来ます。
今回のサンプルパッチはこちらに。
https://github.com/tomoyanonymous/maxmsp_pattr_example
それでは皆様に於かれましてはよいMaxライフを。