puredata で遊んでみよう

前に言ってた [mono-a:r] PureData

元々はMaxっていう信号処理環境のクローンとして作られたっぽくて

 PureDataGUIを活用したプログラミング環境になっています。いろいろなモジュール(オブジェクト/関数)をフローチャートみたいに線で繋いでいくだけでプログラム(パッチ)が完成します。画面上でモジュラー・シンセを操作するような感覚でMIDI/音声の制御が色々とできてしまいます。  具体的には、オシレータ&フィルタ、アナログ式シンセサイザー、各種サンプラーボコーダー、マルチトラック・レコーダ、Ring Modulation,FM synthesis, Waveshaping synthesis, Delay, Flange, Chorus etc.....  さらに、GEM(Graphics Environment for Multimedia)ライブラリーを追加することで、音響信号処理と同じ環境でOpenGLによる3次元コンピュータグラフィックスのリアルタイム制御が可能になります。  また、MIDI信号の加工が出来るので、MIDIイベント・プロセッサーとしてプログラムを組む事もできます。パフォーマンス・ツールとしては最強と言っても過言ではないでしょう。

という感じらしい

いや、これで音楽をやるわけじゃなく プログラミングネタとしてちょっとやってみようという話

まだ、ドキュメントを順に読んでる段階なんだけど

puredata01

これがドキュメント、グラフの部分がプログラムで、そのまま実行できる。

ちょっと複雑なんだけど 上のプログラムは、1から10までカウントアップするプログラム。 モジュールが色々繋がってるね。

各モジュールの上部の端子は入力、下部の端子は出力になっている。

とりあえず、真ん中の列のモジュールだけ見てみよう。 [bang] モジュールをクリックすると、"bang"っていうメッセージが次のモジュールに送信される。 まあ、プログラムスタートみたいなもんだ。

次はtriggerモジュール、とりあえず無視する。

その下は metro モジュール、"bang"と"stop"メッセージを受け取り 決められた周期(ここでは500ミリ秒)で、次のモジュールにメッセージを送る。

次は float モジュールで、floatを格納出来る。 左上はトリガー、ここに何かメッセージが来ると、今保持している内容を下部の端子から出す。 右上は値のセット。

[metro 500] と [float] が繋がってるので、500ミリ秒ごとに [float]モジュールに対して 「おい、次のモジュールにメッセージ(値)渡せよ」 という指示が来るって事になる。

次の [+ 1]モジュールは、来たメッセージ(数値)をプラス1して次のモジュールに渡す。 一番下のモジュールは値を表示だけするモジュールだ、来たメッセージ(数値)を表示する。

[+ 1]モジュールの下部は、3箇所に繋がってる(ちょっと分かりにくいんだけど) 最下部の数値表示モジュール、左側の[select 10]モジュール、そして[float]モジュールの入力だ

今、[float]モジュールに0が入ってるとすると [float]にメッセージが来ると、0が次の[+1]にわたる。 そうすると1が数値表示モジュールに渡る と、同時に、[float]に1がセットされる。(ループバックしてくるような感じ) 500ミリ秒後に、また[float]にメッセージが行くので、1が飛んで2になって帰ってきて となる。

左側の[select 10] は、来たメッセージが10ならメッセージが飛ぶ メッセージが[stop]に渡り、"stop"メッセージが[metro 500]に渡る。 ここで[metro]が停止する。都合カウントアップが10で停止。

さっきスルーしたtriggerモジュールは、複数のメッセージを(右から)順番に出力する。 まず、右側の[0]モジュールにbangが渡るので、結果として[float]に対して"0"が飛ぶ。 要するにfloatが初期化される。 その後に、[metro]に対してbangが飛ぶので、初期化された後にカウントアップ開始ってワケだ

という感じで、下のプログラムも読めるだろう。


で、いじってて気が付いたのは 処理の流れと、データの流れは一致しないんだけど、ついその事を忘れちゃうなぁ

例えば

a = 2;
b = 3;
c = 5;
d = 7;
e = a + b;
f = c + d;
g = e + f;

みたいなのがあったとして、まぁ素直なコードなんだけど 処理の流れは一本道だけど、データの流れは一本道じゃないんだよね

データの流れで考えると

a = 2;                    c = 5;
b = 3;                    d = 7;
e = a + b;               f = c + d;
g = e + f;

こうなる(文字の表現力の限界はセルフサービスで補完してくれ)

いや、別に並列プログラミングまで話を膨らませる気は無いんだけど 日常的なプログラミングで、何が行われてるかというと、まぁデータを使うわなぁ んで、データってのは変数だわなぁ。

・変数に値を格納する ・変数から値を読み取る

っていう、2つの操作しか"変数に対しては"ないんだけど。 プログラミングを考えると

・変数を横に置いておく ・変数を思い出して持ってくる

っていうフェーズがあるんだよね。 今思い返してみれば「どうしてココで詰まるんだろう」っていう謎ポイントのうち この「変数を忘れて思い出す」っていう事が関係してる気がしてきた。

この処理の流れとデータの流れのミスマッチは結構頭が痛い問題で まぁ、処理の流れなんか無いようなプログラミングをすりゃいいのかもしれないけど 普通はそういうわけにも行かないわけで

変数って、難しいよね。

多くのプログラムってのは、割とストリーム指向なんだけど(フローチャートなんか持ち出さなくてもね) データの扱いは、全然ストリームじゃない。 変数ってのはその典型、覚えておいて、途切れて、また持ってくるとか。 全然繋がってない。

関数型言語になると、逆に処理の流れが無くてデータの流れになったりするから面白いんだけど


もうひとつ、配列の最大値を求めるプログラム(良く分かんない擬似言語)

hairetsu = (1, 3, 19, 21, 8, 6);
saidai = 0;
for youso (hairetsu) {
if ( youso > saidai ){
saidai = youso;
}
}

こんな感じ。 これまた良く忘れがちなんだけど、こういうサンプルの変数名は日本語にしよう。 # ネットワークの時はホスト名とかも日本語にしよう。

何故かって言うと、任意の識別子なのか、意味のあるキーワードなのか区別が付かないからだ。 # いや、慣れりゃもちろんつくんだけど。

array = (1,2,3);
max = 0;
for element (array)

とかだと、maxに意味があるのかと思っちゃったりする。 いや、ダサいから正直に言うとイヤなんだけど しかし、重要な事なのです。

こんな設定ファイルのサンプルがあったとして

config foo bar server server baz

前者のserverがキーワードで、後者のserverが任意の識別子(ホスト名)だなんて、見て分かるか? testとかもやめようぜ、分かり辛いから。しかし、アニメキャラ(多分)の名前が並んでるのも勘弁な

っつーわけで、日本語はダサいけど分かり易いんだよね。

閑話休題、で最大値を求めるプログラムのステキな例

hairetsu = (1, 3, 19, 21, 8, 6);
saidai = 0
for youso (hairetsu) {
pass: youso  --------->     receive: morata:
if(morata > saidai)
saidai = morata;
}
}

文字の表現(ry 制御構造自体は変えずに(イテレートを上手くやるとか言う話ではない) データの流れに注目すると、こうなる。 forの仕事は要素を1個ずつ取り出す事であって 最大かどうかを判断するのはforの仕事じゃない

いやね、イテレーターの賢いの使えば出来るじゃないとか思うかもしれないけど 処理フローをやめようって話じゃないんですよ、むしろそれはしっかりやれと。 で、データのフローもしっかりやろうと ただ、両方上手くやる方法が無いから困ったなぁって話。