Max/MSP/Jitter」カテゴリーアーカイブ

UBオブジェクト開発講座 休題

UBオブジェクト開発講座は、3回分が終わったところで一休みです。後から見直すと、他の記事との景観が合いませんね〜特に第3回は記号多過ぎ分量多過ぎでございました(苦笑)。また、投稿逆順に並ぶブログ形式と連載形式も合いませんね。個人サイトに書くほうが良いかもしれませんが、情報が分散するのもイヤなので、DSPマガジン亡き現在、このサイトに集約しておこうと思うわけです。

内容としても、多くの人にとっては興味がないというかチンプンカンプンだとは思うのですが、とりあえず書いておけば、いつか誰かの役に立つかもといった程度なので、軽く流しておいてください。また、この後の展開も、例えばデバッグとかOS APIとかいろいろと考えられるわけですが、さてさて、どうなんでしょうか?

UBオブジェクト開発講座(3) ソース・コードの概要

第3回UBオブジェクト開発講座は、サンプルとして提供されているmaximumオブジェクトを題材に、そのソース・コードの概要を説明します。これは、以前に公開した「Maxオブジェクト開発技法」から主要部を抜き出し、MaxMSP UB SDK用にアップデートしたものです。

日本語コメントを付けたソース・コードは、こちらからダウンロードしてください。
maximum-jcommented.zip

以下は要点のみをザザっと俯瞰しています。

■処理の概要

Maxオブジェクトのソース・コードは、大きく3つの部分から構成されています。

(1) メイン関数 main()【必須】

Max起動時やパッチを開いた時など、オブジェクトが読み込まれた時に呼び出され、クラスとして必要な初期化などの設定を行います。具体的には、以下の2つの処理が必須ですが、必要に応じてその他の処理を行っても構いません。
・クラスの設定を行う。setup()【必須】
・メッセージ処理関数を登録する。【ほぼ必須】

(2) メッセージ処理関数 maximum_bang()など【ほぼ必須】

オブジェクトにメッセージが送られた時に対応する関数が呼び出されます。メッセージ処理関数の引数は、その登録によって異なります。

(3) オブジェクト生成関数 maximum_new()【必須】

オブジェクト生成時に呼び出され、インスタンスとして必要な初期化などの設定を行います。具体的には、以下の3つの処理が必須ですが、必要に応じてその他の処理を行っても構いません。
・オブジェクトを生成する。newobject()【必須】
・アーギュメントの処理、インレット、アウトレットの作成などを行う。
(右から左への順序で。第1インレットはnewobject()が自動的に作成する。)
・生成したオブジェクトへのポインタを関数の戻り値として返す。【必須】

なお、maximumでは必要がありませんが、オブジェクト生成関数において、メモリの確保やデバイスのオープンなどを行った場合は、オブジェクト消滅関数を登録(setup()の3番目の引数で指定)し、その関数でメモリの解放やデバイスのクローズなどの処理を行うようにします。

■メッセージ処理関数の登録

メッセージ処理関数を登録するには、以下の関数を用います。これらはext_proto.hに定義されています。

void addbang(method f); // bangメッセージ処理関数の登録
void addint(method f); // 整数メッセージ処理関数の登録
void addfloat(method f); // 実数メッセージ処理関数の登録
void addinx(method f, short n); // 第nインレットでの整数メッセージ処理関数の登録
void addftx(method f, short n); // 第nインレットでの実数メッセージ処理関数の登録

fはメッセージ処理関数へのポインタ、nはインレット番号(0始まり、第2インレットは1)

void addmess(method f, char *s, short type, ...); // 任意のメッセージ処理関数の登録

sはメッセージ文字列、typeはアーギュメントのデータ・タイプで、アーギュメントの数に応じて並べ、最後は0で終えます。詳しくは、次の「メッセージ処理関数への引数」最後の部分を参照してください。

オブジェクトがメッセージを受け取る時に、登録されたメッセージ処理関数に従って、メッセージやアーギュメントがチェックされ、一致しなければ自動的にエラーとなります。

■メッセージ処理関数への引数

メッセージ処理関数の種類によって、以下のように受け取る引数は異なります。

bangメッセージ処理関数は、次の引数を受け取ります。

void maximum_bang(t_maximum *x)

xはオブジェクト構造体へのポインタを表します。
bangメッセージはアーギュメントを持ちませんから、これ以外の引数はありません。

整数メッセージ処理関数は、次の引数を受け取ります。

void maximum_int(t_maximum *x, long n)

xはオブジェクト構造体へのポインタ、nは送られた整数を表します。

リスト・メッセージ処理関数は、次の引数を受け取ります。

void maximum_list(t_maximum *x, t_symbol *s, short ac, t_atom *av)

sはメッセージのシンボル(list)、acはリストの要素数、avはリストの最初の要素へのポインタを表します。

「jack n m」というメッセージの処理関数は以下のようになります。ここでは、nとmは整数で、nは省略不可、mは省略可能とします。

void maximum_jack(t_maximum *x, long n, long m)

xはオブジェクト構造体へのポインタ、nとmは送られた整数を表します。

なお、このメッセージ処理関数を登録するには、以下のように記述します。

addmess((method)maximum_jack, "jack", A_LONG, A_DEFLONG, 0);

■データ・タイプ

Maxが用いるデータの種類を表すデータ・タイプは、ext_mess.hに以下のように定義されています。

#define A_NOTHING 0 // 無(データはないことを示す)
#define A_LONG 1 // 整数
#define A_FLOAT 2 // 実数
#define A_SYM 3 // シンボル
#define A_OBJ 4 // オブジェクト、アーギュメントの配列
#define A_DEFLONG 5 // デフォルト値が0である整数
#define A_DEFFLOAT 6 // デフォルト値が0.0である実数
#define A_DEFSYM 7 // デフォルト値が""であるシンボル
#define A_GIMME 8 // アーギュメントの配列、t_atomの配列であることをチェック
#define A_CANT 9 // タイプチェック不能/不要であるアーギュメント
#define A_SEMI 10 // ;(セミコロン)
#define A_COMMA 11 // .(コンマ)
#define A_DOLLAR 12 // $(ドルマーク)
#define A_DOLLSYM 13 // $(ドルマークのシンボル)
#define A_GIMMEBACK 14 // アーギュメントの配列、関数でチェックして戻す

■データ構造

Maxのデータ(整数、実数、シンボル)はすべてt_atom(Atom)構造体で表され、データの種類を表すデータ・タイプと実際のデータ値の2つの要素から構成されています。データ値は共用体として、異なるタイプのデータが納められるようになっています。

union word // データ値の共用体
{
long w_long;
float w_float;
struct symbol *w_sym;
struct object *w_obj;
};

typedef struct atom // データの構造体
{
short a_type; // データ・タイプ
union word a_w; // データ値
} t_atom, Atom;

例:t_atom *data の場合、
データの値を参照するには、データ・タイプを確認した上で、データの値を取り出します。例えば、データが整数である場合は、以下のようになります。データ・タイプが明確であれば、データ・タイプの確認は省略しても構いません。

if (data->a_type == A_LONG)
value = data->a_w.w_long;

データに値を代入する場合は、データ・タイプを設定した上で、実際の値を代入します。例えば、整数を代入するには以下のようになります。

data->a_type = A_LONG;
data->a_w.w_long = 100;

UBオブジェクト開発講座(2) オリジナル・オブジェクトの作成

UBオブジェクト開発講座の第2回は、オリジナルのオブジェクトを作成する方法で、要は既存のサンプル・プロジェクトの複製を作り、必要な箇所を変えれば、ハイ出来上がり、というわけです。ここでの説明は「Copying An Example Project」の抄訳を元に、多少の補足を加えています。

Step 1: Finderでmaximumフォルダの複製を作り、フォルダ名を変える。ここではgoofyという名前とする。goofyフォルダは、c74supportフォルダと同じ階層のフォルダ(例えば、example-externsフォルダ)に納めるのが良い。

Step 2: goofyフォルダを開き、Xcodeのプロジェクト・ファイル名を変える。ここでは、goofy.xcodeprojという名前とする。

Step 3: 既に作成しているC言語のソース・ファイルがあれば、それをプロジェクト・フォルダにコピーする。なければ、maximum.cを自分が用いる名前に変える。ここでは、goofy.cという名前とする。

Step 4: Info.plistファイルをダブル・クリックする。Property List Editorが起動して、Info.plistファイルが開かれるので、以下の項目を変更する。
– CFBundleExectuableを作成するオブジェクトの名に変更する。ここでは、goofyとする。
– CFBundleIdentifierをJavaスタイルでの識別子として名前を変更する。ドメイン名を所有しているなら、それを用いる。ここでは、org.akamatsu.goofyとする。

Step 5: Info.plistファイルを保存し、Property List Editorを終了する。

Step 6: プロジェクト・ファイル(goofy.xcodeproj)をダブル・クリックする。Xcodeが起動し、プロジェクトが開かれる。

Step 7: ソース・リストから、不要なファイルを削除する。ここではmaximum.cを削除する。削除を確認するダイアログが現れるので、「参照を削除」ボタンをクリックする。

Step 8: 自分のソース・ファイル(goofy.c)をプロジェクトに追加する。具体的には、Finderからソース・ファイルをドラッグし、左側のリストのSourceフォルダにドロップすれば良い(右側のリストにはドロップできない)。ファイル追加に関するダイアログが現れるので、そのままOKボタンをクリックする

Step 9: プロジェクト・メニューから「アクティブターゲット’maximum’を編集」を選ぶ。

Step 10: 「”ターゲット”maximum””の情報」ウィンドウが開かれるので、一般タブを選び、名前を変更する。ここではgoofyという名前にする。

Step 11: ビルド・タブを選び、構成メニューから「すべての構成」を選ぶ。そして、コレクション・メニューから「パッケージング」を選び、プロダクト名を変更する。ここではgoofyという名前にする。

Step 12: 以上で設定は完了。ビルド・ボタンをクリックすれば、オブジェクト・ファイルが作られる。

Step 13: オブジェクト・ファイルが作成できれば、Maxを起動してオブジェクトの動作を確認する。Defaultフォルダにgoofy.help(あるいはgoofy-test.patなど)といったテスト用のパッチ・ファイルを作ると良い。

実際のオブジェクト開発では、ソース・コードをいかに記述するかが肝心カナメとなるわけですが、これについてはUBオブジェクト開発講座(3)にて説明。

UBオブジェクト開発講座(1) SDKのインストールとサンプルのビルド

細かなことはさておき、ササっとUB(ユニバーサル・バイナリー)オブジェクトを開発する方法を概観してみます。より詳しい説明はCycling ’74のWEBサイトに 「Max/MSP Universal Binary SDK Documentation」が掲載されていますので、参照してください。ちなみに、Windows用のオブジェクトを開発した経験がないので、誰か書いてくれるとウレシイな。

さてさて、UBオブジェクト開発講座の第1回はSDK(ソフトウェア・デベロップメント・キット)のインストールとサンプルのビルドまで、です。これは「Getting Started」の抄訳を元に多少の補足をしています。

Step 1: Xcodeをインストールする

・UBオブジェクトの開発には、Xcode 2.2.1以降とMac OS X 10.4が必要である。
・XcodeはMac OS XのインストールDVDに付属している他、最新版はApple Computer社の開発者用サイトからダウンロードできる。

Step 2: SDKをコピーする

・ Cycling ’74社のWebサイトからMaxMSP UB SDKをダウンロードする。
・MaxMSP UB SDKフォルダをハードディスクの任意の場所にコピーする。

Step 3: フレームワークをコピーする

・MaxMSP UB SDKフォルダのCopyContentsToLibraryFrameworksフォルダの中身をLibrary/Frameworks/フォルダにコピーする。
(Libraryフォルダは日本語環境では起動ディスクの「ライブラリ」フォルダのこと。)

Step 4: サンプルをビルドする

・サンプルのプロジェクト・ファイルをダブル・クリックする。例えば、MaxMSP UB SDKフォルダのexample-externsフォルダにあるmaximumフォルダのmaximum.xcodeprojファイルをダブル・クリックする。
・Xcodeが起動し、プロジェクト・ウィンドウが開く。
・Xcodeのプロジェクト・ウィンドウのビルド・アイコンをクリックして、プロジェクトをビルドする。
・ビルドが成功すれば、maximumフォルダにbuildフォルダが作られ、その中のDefaultフォルダにmaximum.mxoというオブジェクト・ファイルが作られる。
・何らかのエラーが発生する場合は、MaxMSP UB SDKの構成やフレームワークのコピーが間違っていることが考えられるので、再確認する。
・オブジェクト・ファイルが作成できれば、Maxを起動してオブジェクトの動作を確認する。Defaultフォルダにmaximum.help(あるいはmaximum-test.patなど)といったテスト用のパッチ・ファイルを作ると良い。

Step 5: 自分のプロジェクを変換する

・サンプルのプロジェクトを複製し、それを元にユニバーサル・バイナリ化したい自分のプロジェクト、あるいは新しく制作するプロジェクトを作ると良い。詳しくは、UBオブジェクト開発講座(2)にて説明。

scaleとzmapの違い

scaleもzmapも、入力範囲と出力範囲を指定し、その範囲に従って数値変換してくれるオブジェクトですね。zmapは直線変換のみで、scaleは指数変換が可能です。もっとも、scaleのデフォルトの指数係数は1.0で直線変換なので、zmapは要らないじゃん、って気もするのですが、でもscaleはスケーリングで、zmapはマッピングなのです。文字通りですが、この違いが何かと言うと…

scaleはスケーリング(比率計算)なので、入力範囲を超える数値が入力された場合は、出力範囲を超える数値が出力されます。一方、zmapでは、入力範囲外の数値が入力された場合には、出力範囲内に収まるようにクリッピングされます。つまり、zmapでは出力範囲外の数値や出力されません。ということは、数値変換はしなくてもクリッピングのためにzmapを使っても良いわけですね。

scale-vs-zmap.jpg

それから、入力範囲または出力範囲の指定で大小関係が逆である(第1アーギュメントが第2アーギュメントよりも大きな値である、または、第3アーギュメントが第4アーギュメントよりも大きな値である)場合に、scaleはそのままの比率に従って変換を行いますが、zmapは大小関係が正しくなるようにアーギュメントの値を読み替えて変換します。

当然のことながら、どちらのオブジェクトを使うかは適所適材ですから、それぞれの特徴をつかんでおくことが大切ね、っとこれまた学内ネタでしたが、こーゆー小ネタをブログに書くのはどうなんでしょうね?? もちろん、ワークショップ自体はもっと大きな枠でやっていて、そこから小ネタにしやすいことだけを書いているわけなのですが….

sfplay~のプリロード完了取得

今月は学内ワークショップを担当していたり、知人がMaxで仕事をしていたりで、何かとMax絡みの小ネタが多いのですが、その中から今日の課題は、sfplay~でプリロードが完了したことを知るにはどうするか?です。

sfplay~は、ディスクからオーディオ・ファイルを再生するオブジェクトですが、実際にはバッファリングのためにオーディオ・ファイルの一部をメモリに読み込みます。この読み込みが完了した時点を知りたいわけです。なぜ知りたいかと言えば、例えばプリロードが完了するまでに別の要求が発生する場合があるとか、数千回のプリロードの進行状態を知りたいとか、ですね。通常は次々とメッセージを投げておけば順次実行されますが、それでは困る場合に必要になります。

さて、sfplay~のヘルプを見ると、右アウトレットの役目として「bang when cue, file, seek, or list of cues is finished.」と書かれていますが、プリロードが終わった時点でbangが出力されるわけではありません。MSPのリファレンス・マニュアルでも「When the file is done playing, or when playback is stopped with a 0 message, a bang is sent out. 」となっていて、ダメっぽいです。

で、解決はsfplay~だけでは無理で、
解決策:sflist~にpreloadメッセージを送る。
でした。

sflist~はキュー・リスト管理専用のオブジェクトですね。sflist~がpreloadメッセージを受け取り、プリロードが完了すると、sflist~はアウトレットから、preload < キュー番号> < ファイル名>….といったメッセージを出力します。従って、このメッセージを捉えれば良いことになります。

次のパッチでは、オーディオ・ファイルのプリロードが終わり次第すぐに、そのオーディオ・ファイルを再生します。実際の処理は、sflist~の出力からrouteオブジェクトでpreloadメッセージのアーギュメントを取り出し、zl sliceオブジェクトでプリロードされたキュー番号を取り出し、sfplay~への再生開始メッセージとして送る、って流れ。この時、sflist~もsfplay~もアーギュメントに同じ名前を与え、キューを共有するように指定することが必須ね。

preloadplay.gif

ダウンロードはこちらから。preloadplaypat.zip

ちなみに、sflist~が出力するpreloadメッセージのアーギュメントには、キュー番号とファイル名に続いて実数、実数、整数、実数の4つの数値があります。これらは再生開始位置、再生終了位置、再生方向、再生速度のハズです。「ハズ」と書かざるを得ないのは、sflist~の出力はヘルプにもリファレンスにも記載されていないからです(苦笑)。

ファイル名は英数字で!

現行のMaxでは、パッチなどを日本語のファイル名で保存すると、ファイル名が数字になったり、ファイル保存ができなかったりします。また、Finderで日本語のファイル名を付けたファイルをMaxで開こうとすると、ファイルが開けなかったり、タイトル・バーに表示されるファイル名(ウィンドウ名)が文字化けしますね。どのような日本語(2バイト文字)を使うかによって状況は異なるようですが、最悪の場合、パッチの中身が失われることも有り得るようです。そんなわけで、苦労してサンプル・ブラウザを作ったという事情もあるのですが、書籍では書き忘れていました(スミマセン)。正誤表に載せておきます。

というわけで、
解決策:パッチなどのファイル名は日本語を避け、英数字を使おう!
が今週の標語です。

Intel-MacでMSPの音が出ない…

軽くやられました。PowerPC-MacからIntel-Macに移行したところ、MSPから音が出ない、と言うか、出せない。

Maxウィンドウには、
・ error: can’t fragload ezadc~ (missing ), err -4
・ error: can’t fragload ezdac~ (missing ), err -4
・ error: can’t fragload gain~ (missing ), err -4
というエラーが表示され、adc~やdac~も使えない状態。

解決策:(ユーザ)/ライブラリ/Preferences/にあるMax 4.6 Preferences Folderをゴミ箱に捨ててからMaxを起動する。

でした(MaxMSP-MLにて発見)。

移行ツールを使って以前のMacから新しいMacにユーザ・ファイルを転送した場合に、このような問題が起こるようです。移行ツールを使わない(=古いプリファレンスが存在しない)場合は大丈夫なんじゃないかな。

ちょっと面白かったのは、最終変更日が2004年1月であるMax 4 Preferencesや、同じく最終変更日が2006年7月のMax 4 Preferences Folderもあったことで、以前のマシンでの変遷が分かりますね。

簡易なパーティクル表示

学内ネタからなのですが、2061:Maxオデッセイで取り上げているパーティクルの描画(p.848-850)は、球体を用いているので、結構処理が重いようです。球体は多数のポリゴンから成っているからですね。従って、処理能力(特にGPUパワー)によっては、数十個のパーティクルを描こうとするとフレーム・レートが落ちてしまいます。

そこで、できるだけ処理負荷が少ないような簡易なパーティクル表示に変更してみました。このパッチでは単純な点でパーティクルを描きます。実際の処理としては、レンダーの描画方法(primitiveアトリビュート)をpointsにして、各点の座標(ジオメトリー・データ)を納めたマトリックスをレンダーに送るだけです。これは、パーティクル関係のヘルプ・パッチで使われる表示方法と同じですね。

06-13-01-simple-1_m.gif

この方法の欠点は、一定の大きさの点(正方形)で描画するので、奥行きによって点の大きさが変わらず、奥行き感の表現に欠けることです。とは言え、処理負荷はかなり低くなりますので、G4クラスでも2〜3万個のパーティクルを使ってもヘッチャラですね。この場合、emitも大きな値にしなければ意味がないですけどね。

06-13-01-simple-2.gif

ついでに、パラメータの設定をひとつのサブ・パッチにまとめ、jit.pwindowではなくjit.windowで表示するようにしています。パッチのダウンロードはこちらからどうぞ。06-13-01-simplepat.zip

リブチンスキー効果

スリットスキャン(映像から一定のルールで1ラインずつ画像を抜き出して再構成する)手法のひとつで、私が勝手にリブチンスキー・エフェクトと読んでいるパッチです。パッチのダウンロードはこちらからどうぞ。rybczynski-effectpat.zip

rybczynski-effect.gif

メールで質問があったのですが、IAMASの学生からも時々聞かれるので、意外と分かりそうで分からない手法かも。jit.matrixsetにライン数分の画像(マトリックス)を溜め込みながら、ラインを変えてjit.matrixに再構成するのがミソね。

元ネタはZbigniew RybczynskiのThe FOURTH DIMENSIONで、なんと1988年の作品。パッチは以前にサンレコに書いた記事からです。入手困難だと思いますが「コードリソース」という本では、同じ処理をC言語で書いています。