チュートリアル26

MIDIで映像をコントロールする


MIDIと映像の関係

Maxが始めに開発されたとき、Maxは主にMIDIを用いてインタラクティブに楽器をコントロールするものでした。コンピューターの処理速度が上がってくるにつれて、MSPを使用してオーディオ信号が直接Max上で生成できるようになり、 そしてJitterを用いて映像のような沢山のマトリックスデータが処理できるようになってきました。Maxの力が偉大なのは、それらの分野のデジタルデータ(MIDI、オーディオ、映像)の全てを操作することができ、そしてそれらを相互に結びつける事が出来るからです。このチュートリアルでは入力されるMIDIデータで、Jitterでの映像の再生をコントロールする事に焦点をあてて見ていきます。

MIDIで映像をコントロールすると、主に2つの利点を得ることが出来ます。

1.画面上をマウスで操作するのではない、フィジカルインターフェース(スライダーやホイールなど)を使用することが出来ます。

2.リアルタイムのパフォーマンスで、音と映像の間に面白い関係を作り出すことが出来ます。

MIDIコントローラーには映像のコントローラーとして使用できる物も沢山あります。複数のスライダーのバンクや、フェーダーボックス、デジタルミキサー、ジョグホイール等です。キーボードのついたシンセサイザーには大概はボタン、スライダー、ホイール、そしてフットペダルがピアノ様の鍵盤と共に付属してきます。このチュートリアルではそれら多くには触れず、一般的に用いられているMIDIキーボードを扱います。

このチュートリアルでは、MIDIのセットアップがMax TutorialやMSP Tutorialで使われている設定であると仮定して進みます。つまり、a)OMSがインストールされていて、OMS Setupを使用してOMSの設定がされており、MaxのファイルメニューにあるMidi SetupからMaxの仮想ポートをOMS Setupで設定したデバイスに設定してある。b)モジュレーションホイールと、ピッチベンドホイールがついている61鍵のMIDIシンセサイザーキーボードを使用する。c)キーボードはMIDIインターフェースに接続されており、Maxの仮想ポートaに設定されている。

このチュートリアルパッチでは、映像を演奏します。MIDIキーボードからのノートを使用してムービーの再生をコントロールしたり、様々な種類のMIDIメッセージを使用してリアルタイムで映像にエフェクトをかけたり調整したりといったことをしていきます。


MIDIデータをマッピングして映像コントロールのパラメーターとして使用する

MIDIのチャンネルメッセージ(ノート、ピッチベンド、アフタータッチなど)のデータは0から127までの値です。ほとんどの場合、Jitterのアトリビュートは、アーギュメントとして0から1までの値を用います。特にデータタイプcharでプレーン数が4の2Dマトリックスを使って映像を扱う時などはこれに当てはまります。このため、始めにしなくてはいけないことの1つとして、Jitterオブジェクトをコントロールするのに適切な範囲にMIDIデータを変換しなくてはなりません。このチュートリアルパッチではこれに対するいくつかの方法を説明します。

・Jitter Tutorialフォルダからチュートリアルパッチ26jMIDIControl.patを開いてください。

このパッチの黄色い部分で、ポートaに繋がれたキーボードコントローラーからの様々な種類のMIDI入力を受けています。ctlin a 1 はモジュレーションホイールからの値を、ctlin a 7 はボリュームペダルからの値を、bendin a はピッチベンドホイールからの値を、notein aはキーボードからの値をそれぞれ受けています。モジュレーションホイールやボリュームペダルの様なコントローラーは連続した値を出力するので、MIDIデータの変換が最も簡単です。それらの値を0から1の範囲にマッピングするのはとても単純な事で、単に127で割るだけです。


MIDIのコントロールデータを0から1の範囲に変換する

どちらのコントロールデータも0から127までの範囲の値です。モジュレーションホイールが"標準"で止まっている時の値は0です(モジュレーションをかけていない状態)。一方、ボリュームペダルが普段止まっている位置での値は0ではなく100や127のような値です(ボリュームがある状態)。これらは、映像のエフェクトをコントロールするのに、それぞれ少しずつ違う方法で使用出来るかもしれません。

ピッチベンドホイールは、また違った標準の状態を持ちます。標準で止まっている時の値は64で、ユーザーがホイールを離すとこの位置に戻ります。このため、止まっている状態の値で0.5になるようにしたいのですが、困ったことに64は0と127の丁度中心ではないのです。もし単純に127で割ってしまうと、64のベンド値は0.504になってしまいます。よって、ここでは下の例の様に、下方向のベンド値と上方向のベンド値に対して別々に処理をしています。


図-64の中心の値から下方向には64個のピッチベンドの値があり、上方向には63個の値があります

キーボードからのピッチに関する情報では、問題はさらにやや複雑です。まず第一に、ほとんどのキーボードでは0から127までに対応する鍵盤をもっていません。普通の5オクターブのキーボードではMIDIノートナンバーの36から96までの値を出力します。さらに重要なのは、私達はノートナンバーを音の"高さ"(0から127までの幅を持った値)としてだけではなく、音楽的に意味を持った"音高"(C、C#、D等)として認識しているという事です。このパッチでは、どちらの方法でもピッチが見られるようにしてあります。ノートナンバーの36から96までの範囲の値を0から1までの範囲の値にし、% 12オブジェクトを使用して音高を得ています。(全てCは0、C#は1というようになります)


ノートオンの時のノートナンバーを使って音の高さと音高を得る

上の例では、扱いやすいオブジェクトを使用して、入力されるノートナンバーの値の範囲を小さな値の範囲に変換しています。poly 1 1オブジェクトは一度に一つだけのノートオンメッセージを通過させます。このオブジェクトは次のノートオンメッセージを受信すると、自動的に直前のノートオンメッセージに対するノートオフメッセージ(同じノートナンバーでベロシティが0のメッセージ)を送信します。今回は一度に一つだけのノートナンバーをトラッキングしようとしています。それは、ユーザーがレガート奏法(前の鍵を離さない内に次の鍵を押す)で弾いたり、コードのいくつかのノートを"同時に"(ほぼ同時です。MIDIに置いて本当に同時という事はあり得ません)弾いたりすると、実際にこのパッチがどのノートの値をトラッキングしているのか分かりにくくなるからです。poly 1 1オブジェクトを使用することで確実に、現実のMIDIキーボード上でまだ押さえていても、一番最後に押されたノート以外の全てのノートはオフの状態になります。stripnoteオブジェクトはノートオフメッセージを遮断し、ノートオンメッセージのみがこのオブジェクトを通過します。これはトラッキングしたい瞬間が、ノートがオフになる時ではなく、ノートがオンになる時のみだからです。

ノート:このパッチはMIDIキーボードを使用しなくても試せるように作られています。画面上のksliderオブジェクトの鍵盤をクリックすることで、擬似的にノートを演奏する事が出来ます。また、Mod.、Vol.、Bend.、Key、そしてVel.とラベルのついたナンバーボックスをドラッグすることでその他の値も操作する事が出来ます。言うまでもなく、マウスでの操作感は、フィジカルインターフェースであるMIDIキーボードにはやや劣るでしょう。しかし、少なくともパッチや、この章で説明されている事を試す事は出来ます。

・MIDIキーボード(とホイール)を試して、MIDIメッセージがMaxに入力されている事を確認してください。入力されない場合は、MIDI入力のオブジェクトをダブルクリックして適切な入力デバイスを選択してください。


sendreceiveを用いる


このパッチの仕組みを、メッセージの流れを追って理解するのは少し難しいかもしれません。なぜならこのパッチには沢山のsendreceiveオブジェクトが使われているからです。これらのオブジェクトは主にパッチコードが複雑に絡み合わないようにするために使われています。ここではjit.qt.movieオブジェクトを中心として、非常に沢山のメッセージを パッチ中の様々なオブジェクトと入出力するので、これらのオブジェクトを使用してパッチコードが煩雑になるのを回避しているのです。従ってjit.qt.movieオブジェクトは受信も送信も全てreceivesendオブジェクトで行っています。そしてこれらのオブジェクトを使用することで、パッチ中の他の全てのオブジェクトも相互に送受信し合う事が出来ます。


どこにいるかに関わらずjit.qt.movieと通信する事が出来ます

メッセージボックス中のセミコロン(;)の使用方法に慣れていない人の為に、少し、セミコロンの使用方法について説明します。メッセージボックスの中に、セミコロン、receiveオブジェクトの名前を書き入れ、その後にメッセージを書きます。すると、そのメッセージボックスはsendオブジェクトにメッセージを送信した時と全く同じ働きをします。下の例を見てください。


メッセージボックス中のセミコロンはsendオブジェクトと同じような動作をします

それではreadメッセージをjit.qt.movieオブジェクトに送信する際に何が起こっているか、順を追って見ていきましょう。

・;movieA read blading.mov;movieA vol 0と書かれた、赤いメッセージボックスをクリックしてください。

このメッセージボックスをクリックするとムービーblading.movが読み込まれます。jit.qt.movieはムービー読み込みの処理が終わると、右アウトレットからreadメッセージを送信します。ファイルの読み込みが正常に完了すればread blading.mov 1というメッセージが送信されます(正常に完了しなかった場合は数値の部分が1以外のものになります)。このメッセージはパッチ左下の薄い紫色の部分にある、receive Arightoutletオブジェクトに送信されます。そのあとrouteメッセージで全てのメッセージを分類して適切な送信先を決めています。jit.qt.movieからのreadメッセージはその後、パッチ右下の緑色の部分にある
r readオブジェクトへ送信されます。そしてzl ecils 1オブジェクトとsel 1オブジェクトを用いて、メッセージの最後の数値が1かどうかをチェックできるようにしています。もし1が表示されていたら正常にムービーが読み込まれたという事です。それでは次へ進んでムービーのアトリビュートを調べてみましょう。


ムービーが正常に読み込まれると、framecount、fps、rate、そしてdimアトリビュートを調べる

readメッセージのアーギュメントで残っているのはムービーのファイル名だけです。このファイル名をメッセージボックスに表示させて、今再生されているムービーのファイル名が分かるようにしてあります。そして読み込みが正常に行かなかった場合、このメッセージボックスにはfailed to read movieというメッセージがファイル名の代わりに表示されるようになっています(delayオブジェクトを使って、このメッセージが確実に、ファイル名より後にメッセージボックスに入力されるようにしています)。


MIDIノートを使ってビデオクリップの操作をする

このパッチではMIDIキーボードで弾かれたノートの音高で、映像の再生地点を決めるようにしました(もちろん、MIDIを使用してムービーの再生地点を変更したり、他のムービーを再生したりするやり方は沢山あります。ここで紹介されているのは、たまたま私達がこのチュートリアルの為に選択したやり方というだけのものです。後のチュートリアルでは一つの映像から別の映像に切り替える方法を見ていきます)。そこでムービーのトータルのフレーム数を調べて(jit.qt.movieオブジェクトのframecountアトリビュートです)、12で割っています。そうして得られた音高の値(ノートナンバー % 12)を使用して、ムービーの中の12の地点へ再生地点を移動します。

ムービーblading.movは1秒のシーンを12個含んだ12秒の映像です。よってこの場合は各音高で、この短いシーン一つ一つの先頭に再生地点を移動するようにしましょう(もちろんここまで綺麗になっているのは、そうなるように作ってあるからです。しかし、このパッチはムービーの実際のフレーム数を使用して、どんな長さのムービーでも必ず12個に分割出来るように作られています)。


中央のCの上の音高F(5)は、フレーム数75、12シーンの中の5番目を指しています

ノートオンの際のベロシティの値がトグルボックスをオンの状態にしています。そしてmetroから jit.qt.movieにbangが送信されます。そしてノートオフのベロシティがトグルボックスをオフにしています。

・Show/Hide Display Windowとラベルが付けられたトグルボックスをクリックして、Displayウィンドウを表示してください。MIDIキーボード(またはksliderオブジェクト)でいくつかのノートを演奏して、ムービーの再生地点をいくつかの点に飛ばしてみてください。

jit.qt.movieオブジェクトから出力されるマトリックスはsend Aleftoutletオブジェクトから送信され、最終的にreceive displayオブジェクトで受信され、jit.pwindowに送られます。それではjit_matrix メッセージは、send Aleftoutletから、receive displayまでの間、どのような処理をされているのでしょう?それらのマトリックスはビデオエフェクト処理のサブパッチへ送られています。しかしそのサブパッチを試す前に、まずそれらのエフェクト処理のコントロールがどんな意図で作られているかを説明します。


コントロール情報をルーティングする

このチュートリアルの始めの方で、MIDIで得られた情報をJitterのアトリビュートをコントロールするために0から1までの値に変換する、様々な方法について見てきました。このパッチの黄色い部分を見ると、MIDIから得られたコントロールの情報が、5つのsendオブジェクトに送信されていることがわかります。
s pitch、s vel、s bend、s mod、そしてs volの5つです。これらはそれぞれ異なった5種類のMIDIコントロールです。それらを使用して最大8つのビデオエフェクトを直列に繋いたものをコントロールしようとしています。

ムービー → レートコントロール → ボリュームコントロール → 彩度コントロール → エッジディテクション → ピクセレーション → ズームアウトコントロール → ウィンドウに表示

自由度を最大限にするため、どのMIDIコントロールの情報を使用しても全てのエフェクトをコントロール出来るようにしたいと考えました。このパッチでは、こういった時のためにデザインされた、matrixctrlオブジェクトとrouterオブジェクトを使用しています。routerオブジェクトは複数のインレットで受信したメッセージを、内部的にルーティングして、指定したアウトレットから送信します。matrixctrlオブジェクトは(このオブジェクトはMSPのmatrix~オブジェクトとMaxのrouterオブジェクトをコントロールするために作られており、このオブジェクト単体ではJitterのマトリックスを操作するのに向きません)それらの割り振りの情報を編集する為のユーザーインターフェースオブジェクトです。パッチの中の青い場所にあるmatrixctrlオブジェクトを見てください。


5番目の入力(VOL)を2番目の出力(Volume)に割り振っています

matrixctrlオブジェクトは入力元をグリッドの縦線に、出力先をグリッドの横線として表示します。よって、routerオブジェクトの5番目のインレットに入ったメッセージを2番目のアウトレットから出力したいときには、グリッドからそれらの条件にかなった所をクリックすれば良いのです。この例では、routeオブジェクトによって、volデータがVolumeのエフェクトをコントロールするように設定しています。matrixctrl のグリッドから必要な場所をクリックすると、routerオブジェクトにメッセージが送信され、入力元から出力先への割り振りがrouterオブジェクトの内部に作られます(もう一度クリックすると赤い点が消えます。 router内部の割り振りもその通りに更新されます)。このパッチでは、routerオブジェクトはpatcher effectsサブパッチの中にあります。もしこのオブジェクトがサブパッチの中でなく、matrixctrlと同じ所にあったとすると、それらは下の例のように繋がります。


routerはMaxメッセージの"パッチベイ"で、matrixctrlはそのユーザーインターフェースです


パッチのまとまりごとにルーティング(もしくはバイパス)をする

いくつかのビデオエフェクトを使用しない(例えばズームやピクセレーションを使用したく無い時もあるかもしれません)時には、それら特定のエフェクトをバイパスする必要があります。effectsサブパッチの中には、Ggateオブジェクトとgateオブジェクトが使用されており、これらのオブジェクトを使用してバイパスを行っています。使用するエフェクトと使用しないエフェクトの設定を簡単に行えるように、このパッチには、メインのパッチにradiogroupオブジェクトを使用したチェックボックスが作られています。ユーザーがチェックボックスをクリックすると、radiogroupオブジェクトは全ての項目のオン/オフの状態をアウトレットから送信します。そしてその情報を使ってサブパッチの中のGgateオブジェクトを切り替えています。


ズームイン、ピクセレート、ズームアウトのエフェクトは完全にバイパスされます

・patcher effectsオブジェクトをダブルクリックして、effectsサブパッチの内容を見てください。


radiogroupオブジェクトの出力を使って、gateオブジェクトとGgateオブジェクトを切り替えます

サブパッチの中のreceive Aleftoutletオブジェクトはメインパッチのjit.qt.movieオブジェクトから送信されたjit_matrixメッセージを受け取ります。上の例では、Ggateオブジェクトによって、jit_matrix メッセージがp brightnessサブパッチをバイパスするように、そして次のGgateによってp saturationサブパッチを通過するようにルーティングされています。このように、Ggateオブジェクトは各エフェクトに対する挿入/バイパスの切り替えを行っています。そしてradiogroupオブジェクトのチェックボックスは、それらを切り替えるためのユーザーインターフェースとして機能しています。この直列に繋がれたエフェクトの最後に、マトリックスはやっとsend displayオブジェクトに送信され、そしてDisplayウィンドウにたどり着くことになります。

こうして、コントロールデータのルーティングとエフェクトのルーティングを、メインパッチの中でユーザーが別々にコントロール出来るように作られています。matrixctrlオブジェクトを使って、(1つ以上の)入力からのMIDIデータを、(1つ以上の)エフェクトにルーティングする事が出来ます。そして radiogroupチェックボックスを使用して、全エフェクトから一つ一つの挿入/バイパス設定をする事が出来ます。

・[effects]サブパッチウィンドウを閉じてください。チェックボックスを使用して挿入したいビデオエフェクトを選択し、matrixctrlを使用してMIDIからの入力をそれらのエフェクトに割り当ててください。いくつかの組み合わせを試して、どのエフェクトにはどのタイプのコントローラーが最も直観的に(そしてキーボードでパフォーマンスをした時に)操作出来るか見てください。

ビデオエフェクトをコントロールする

このチュートリアルのそれぞれのビデオエフェクトはとてもシンプルなものなので、ここでは一つ一つについては取り上げません。ここでは、リアルタイムにビデオエフェクトをコントロールする、その方法を決定するのに役にたついくつかの特徴について説明します。

・もう一度patcher effectsオブジェクトをダブルクリックしてeffectsサブパッチウィンドウを開いてください。典型的なビデオエフェクトのサブパッチ内を見るために、p brightnessオブジェクトをダブルクリックしてください。


0から1までの範囲のデータは、明度をコントロールするために0から2までの範囲にスケーリングされます

このサブパッチでは、jit_matrixメッセージがサブパッチの左インレットから入力され、コントロールする為のデータが右インレットから(0から1までの範囲で)入力されます。コントロールのデータは、適切な範囲にスケーリングされ、Jitterオブジェクトのアトリビュートを設定します。そして処理されたマトリックスは次のエフェクトのサブパッチへと出力されます。p saturationとp zoominサブパッチはほとんど同じ構造を持ちます。p zoomoutサブパッチもほぼ同じ構造を持ちますが、下の例のように多少の計算処理を含んでいます。


0から1まで直線的な増加をするデータは、1から0.125まで指数関数的のカーブを描く値に変換されます

上の例では、入力されるコントロールのデータ(0から1まで)で、2のべき乗を計算しています。コントロールデータが0の場合は2の0乗で、値は1になります。コントロールデータが1の場合は2の-3乗で、値は0.125 となります。このようにして実際のコントロールの値は増減の方向が逆転するように、そして直線ではなく指数関数カーブで変化していくように操作されます。

p edgesサブパッチでは、ソーベルエッジ検出を行うjit.sobelオブジェクトを使ってエフェクト処理を行っています。このサブパッチでコントロールしているのは、元々入力されたイメージとエッジ検出の出力イメージのミックスの部分だけです。このために、jit.xfadeオブジェクトのxfadeアトリビュートをコントロールしています(チュートリアル8で詳細に解説されています)。

p pixelateサブパッチはマトリックスのディメンジョン数を減らす処理です(ダウンサンプリングとして知られる処理です)。全体的にデータが間引かれ、320x240のDispayウィンドウで見るとイメージの画素が目立つようになるはずです(このピクセレーション処理についてはチュートリアル14で説明されています)。このパッチでは、元のjit.qt.movieオブジェクトからディメンジョンのデータを取り出しています(ムービーの読み込みの時に取り出しています)。そしてコントロールデータは計算処理で1から0.0625までの範囲の値に変換され、取り出したディメンジョンのデータをスケーリングするのに用いられます。新しくスケーリングされたディメンジョンのデータはjit.matrixのdimアトリビュートを設定するのに使われます。下に例があります。


ダウンサンプリングしたイメージを再びアップサンプリングすると、ピクセレート処理になります

p rateとp volumeサブパッチは他のサブパッチと少し違います。これらのサブパッチ内では実際にはマトリックス処理を行っていません。ここではメインパッチのjit.qt.movieのアトリビュートを変更しているだけです。下の例はp rateサブパッチの内容です。


計算の結果を、全てのr rateオブジェクトに送ります。
jit.qt.movie
オブジェクトのrateアトリビュートの設定も行っています。


まとめ

MIDIコントローラーを使用したフィジカルインターフェースは、Jitter上でリアルタイムに映像をコントロールするのにとても向いています。特に映像と音楽が関係していればなおさらです。色々なタイプのコントローラー(キーボード、ピッチベンドホイール、ボリュームペダル、etc)はそれぞれが違ったデータの範囲を持っており、それぞれに異なった変換が必要になります。全てのMIDIチャンネルメッセージは0から127までの範囲の値をとります。これは、Jitterのアトリビュートをコントロールする際には、作成しようと思っているエフェクトをどうするかによって様々な方法で変換する必要があります。このパッチでは、まず始めに全てのMIDIデータを0から1の範囲の値に変換し、その後でそれぞれのビデオエフェクトごとに範囲を変換しなおしています。

Jitterのオブジェクトは非常に多くのメッセージを受信するので、必要なメッセージを構成するためにメッセージボックスを使う事が多くなるでしょう。パッチ中の様々な場所から同じ場所にむけてメッセージを送信しなくてはならない時、メッセージボックスがパッチコードを使用せずにメッセージを飛ばせる事を思い出すと良いかもしれません。 セミコロンの後にrecieveオブジェクトの名前を書くことで、パッチ中でパッチコードがスパゲッティの様な状態になることを防げるでしょう。

色々な所から来たMaxメッセージをまた色々な場所に送りたい場合、そしてメッセージをそれぞれの目的の場所へとルーティングしたい場合は、routerオブジェクトをMaxメッセージの"パッチベイ"として使うことが出来ます。matrixctrlオブジェクトはrouter内部の割り振りを設定するための完成されたインターフェースです。matrixctrlrouterを使用して、5つのMIDIコントロールデータのそれぞれと、8つのビデオエフェクトのそれぞれを結びつける事が出来ます。また、このパッチではradiogroupオブジェクトを使用して、ビデオエフェクトの挿入/バイパスを設定するチェックボックスを作っています。