この章の目的はPdのデザインと、どのように動くようになっているかを記述することです。 Pdの入手、インストール、実行についての実技的詳細は次の章に書かれています。クリックさせたり波形を折りたたんでしまわないようにしたりするようなディジタルオーディオ処理の基礎を学ぶには Dodge and Jerse の Computer Music が良い参考文献です。
オーディオ入出力それぞれにピークレベルメータとクリップインジケータがあり、入出力チャンネルのすべてを反映したピークレベルを入力、出力について教えてくれます。注意して欲しいのはDC(訳注:Direct Current)が入力レベルとなって表れることです。多くのサウンドカードのDCレベルは50代です。RMS(訳注:Root Mean Squared)値でオーディオレベルを見るにはヘルプウィンドウ(訳注:Helpメニューのことでしょう)の"test audio and MIDI"を選択してください。メインウィンドウの表示は入出力のクリッピングを防ぐことだけの為に作られたものです。ピークメータのオン/オフは左下のチェックボックスでできます。
右下のチェックボックスはグローバルな(訳注:現在実行中のPd全体の)オーディオ処理のオン/オフを切り替えます。オーディオをオフにしても、オーディオデバイスを開放するわけではなく、計算を停止するだけです。"audio"メニューでも切り替えられますし、ショートカットキー"Control-."でオーディオ処理をオフに、 "Control-/"でオンにできます。オーディオがオンになっているとき、Pdはあなたが開いている全てのパッチ(見えているかどうかは関わりません)に基づくオーディオサンプルをリアルタイムに計算しています。
入出力に同期エラーが発生した場合、DIO(Digital I/O)エラーインジケータがフラッシュします。クリックすると最近発生したエラーのリストを表示します。このインジケータは通常Pd起動時に読み込まれ、(DAC FIFOがあふれた、または(かつ)、ADC FIFO が空になったために)計算が遅れたときにいつでもインジケータが赤くなります。また、オーディオ入力とオーディオ出力が同じサンプリング周波数でないときにも赤くなります。 audio and MIDI support を参照してください。
Pdの書類は"patches"(パッチ) または "canvases"(キャンバス)と呼ばれています。開いた書類は、1つのメインウィンドウと数は決まっていないサブウィンドウからなっています。サブウィンドウは開いておいたり閉じておいたりできますが、あなたに見えているかどうかに関わらずに動いています。ここに単純なPdのパッチを挙げます:
このパッチには次に挙げる4つのテキストボックスがあります。1つの(0が表示されている)ナンバーボックス、1つの"print"と書いてあるオブジェクトボックス、そして、2つのコメントです。ナンバーボックスとオブジェクトボックスは、ナンバーボックスの出力からオブジェクトボックスの入力へとつながっています。ボックスには0以上の入出力があり、入力は上に出力は底(下)にあります。
Pdのプリントアウトは標準出力に現れます。通常、あなたがPdを起動した"shell"か"terminal"(仮想端末)を開いたままにしておけば、プリントアウトやエラーメッセージを見ることができます。
Pdのパッチには次に挙げる4つのボックスがあるでしょう。オブジェクトボックス、メッセージボックス、GUIボックス、そしてコメントです。
オブジェクトはオブジェクトボックスにテキストを書き込むことで作ることができます。テキストは空白文字(ホワイトスペース)でアトムに分割されます。 一番左のアトムはPdが作るオブジェクトの型を特定します。そして、他のアトムはクリエーション引数(creation arguments)と呼ばれ、Pdにどのようにオブジェクトを初期化するかを伝えます。例にあなたがこう書いたとしましょう、
"+"はオブジェクトのクラスを特定します。この場合だったら、このオブジェクトは足し算をする種類のオブジェクトでしょう。そして"13"は足す数の初期値としてセットされます。アトムは数字列か、"+"のような記号列(symbols)(訳注:文字も記号列に含まれます)のどちらか一方で成り立っています。オブジェクトボックスに書かれたテキストはオブジェクトにあるインレットとアウトレットの種類と数量を確定します。 "+"等の一部のクラスではインレットとアウトレットは固定されて配列されています。ということは、その他のクラスでは、インレットとアウトレットはクリエーション引数に依存しているということです。
以下に単純なMIDIシンセサイザーの例を挙げます:
このパッチは、コントロールオブジェクト(notein、stripnote、mtof)とチルダオブジェクトであるosc~、*~、dac~によって構成されています。コントロールオブジェクトはそれぞれの機能を散発的に実行し、結果として1つ以上の種類のイベントを生じます。この例では、MIDIノートメッセージを受け取るとコントロールオブジェクトの処理を作動させます。ノートメッセージが発生するのはノートオンのときであってノートオフのときではありませんが、コントロールオブジェクトの処理の結果、周波数をHzで計算し、オシレータ("osc~")に伝えます。
このパッチの後半にはosc~、*~、dac~があり、アナログシンセサイザーの様にオーディオサンプルを計算します。osc~オブジェクトはコントロールメッセージを自分の周波数とし、"*~"にオーディオシグナルを送るという点で二つの仕組みのインターフェイスとして働きます。オーディオシグナルは散発的ではなく、連続した数値列(ストリーム)です。結果として、チルダオブジェクトはコントロールオブジェクトとは全く異なったルールで動きます。パッチのオーディオ部分はMIDIメッセージが来ようが来まいが、いつも動いています。コントロール処理の機能とは、オーディオ処理の間に、オシレータの周波数等のオーディオ処理のパラメータを変更する処理を挿入することです。
ボックスの「枠」を見れば、その中のテキストがどのように解釈されるか、そしてそのボックスがどのように機能するかが分かります。オブジェクトボックスはあなたがパッチをロードしたときにその中のテキストを使ってオブジェクトを作ります。メッセージボックスはテキストを送信するメッセージとして解釈し、活性化されたとき(メッセージが届いたときかマウスでクリックされたとき)にいつでもメッセージを送信します。以下の例では、メッセージボックスがクリックされると"21"というメッセージが、受け取ったメッセージに13を加えるオブジェクトボックスに送られます。
3つ目に表示されているボックスはGUIボックスです。GUIボックスは(この例の)ナンバーボックスを含むたくさんの形があります。トグル、スライダー、などです。パッチの実行中にオブジェクトボックスやメッセージボックスの見た目が変わらないのとは裏腹に、ナンバーボックスの内容(テキスト)はボックスが持っている現在値を反映して変化します。クリックして上下にドラッグしたり、値を打ち込んだりしてナンバーボックスをコントローラとして使うこともできます。(Shift-click alt-click による操作もあります。 getting help を参照して、調べ方を見つけましょう。)
シンボルボックスというのも作れます。これはナンバーボックスのようですが、"cat"のような記号(シンボル)を扱います。(最後に"enter"が入った)文字列(strings)を書き込んだり、メッセージとしてインレットに受け取った文字列(strings)を表示するのに使えます。
パッチをファイルに保存するとき、Pdはパッチにある全てのオブジェクトの全ての状態を保存するわけではありません。しかし、オブジェクトのクリエーション引数とオブジェクトの接続は保存されます。いくつかデータストレージオブジェクトはファイル読み書きのための機能があったり、内部状態を復元することができたりします。PdはPd起動時のパラメータの一部として特定されたpath を用いてファイルを探します。pathは1つかコロン(windowsではセミコロン)によって分けられたそれ以上のディレクトリを特定します。ほとんどのオブジェクトはsearch path(検索パス)に沿って読み込まれますが、Pdがファイルを書くときは、パッチがあったディレクトリに行きます。
パッチはエディットモードかランモードのどちらかの状態にすることができます。これらのモードが影響を与えるのは本当にマウスクリックのパッチに対する効果にだけです。 エディットモードでは、クリックとドラッグはボックスを選択したり、移動したり、接続を切断したりできます。ランモードでは、ボックスをクリックするとそれぞれ違った方法で反応するメッセージをそのボックスに送ります。ランモードでは、ナンバーボックスやメッセージボックスは、コントローラとして使うことができます。通常、あなたがパフォーマンスの最中なら、ランモードにしておくでしょう。パッチを編集するときにはエディットモードに行きます。
"put"メニューを使えば、ボックス(オブジェクト、メッセージ、GUI、コメント)を作ることができます。 便利なショートカットも覚えましょう。オブジェクトボックスとメッセージボックスは最初は何もかかれていません。好きなところへドラッグして、テキストを書き込みます。GUIオブジェクト(これにはいろいろな趣があります。)には書き込む必要がありません。ただ作って位置を決めて、置いてください。
"Put"メニューを使うよりもボックスを選択して(Editメニューで)"duplicate"したほうが便利な場合にたくさん遭遇するでしょう。複数のボックスを選択してduplicate(複写)すると、それらの接続も再現されます。
Pdウィンドウのボックスはクリックすることで選択することができます。1つよりも多くのオブジェクトを選択するには、shift-clickか、「外側」をクリックしてドラッグしてできた短形内のすべてのオブジェクトを選択します。line(訳注:パッチコードのことでしょう)は選択できません、ボックスだけです。非選択オブジェクトをクリックすると、オブジェクトボックスやメッセージボックスやコメントの文字はアクティブに、つまり、テキストを編集できる状態になります。短形を使って選択をしても、テキストはアクティブになりません。これは、さらにクリックすると、オブジェクトを移動するか、その中のテキストを選択するかに影響を与えます。
中のテキストをアクティブにしないでボックスを選択したら、バックスペースキーを押せばそのボックスを「削除」することができます。 メニュー項目で、"cut"(切り取り)、 "copy"(コピー)、 "paste"(貼り付け)することができます。気をつけてほしいのは、ペーストして新しいオブジェクトを置くと、徹底して古いオブジェクトよりも上に配置されるということです。"duplicate"はコピー&ペーストを短い間に行い、新しいボックスを作ります。複数のボックスを一緒に画面の新しい場所にドラッグすることができます。Pdの中の複数のウィンドウ間でカット&ペーストすることができますが、OSのカット&ペーストとは別物です。文字列のカット/コピー/ペーストはまだ実装されていません。とは言え、LinuxとIrixでは少なくとも"X-ペースト"を"text"ダイアログから/へすることができます。("text"ダイアログは"edit text"メニュー項目から 作ることができます。)
テキスト項目を変更するには、それを選択してからテキストを編集します。1回だけクリックすると、テキスト全体が選択され、それらはすべて打ち込んだテキストで置き換えられます。もう1回クリックしてドラッグすると書き換える部分を選択できます。
テキストの量が少しではないとき(例えば、コメントで)、テキストを選択して、Editメニューの"text editor"を選択したくなるかもしれません。すると、そのテキストが表示されたテキスト編集ウィンドウが開きます。そのウィンドウの"send"を押すと、Pdにテキストを再入力するのと完璧に等価です。しかも1つより多くのボックスに次々と好きなだけsendすることができます。
ボックスをクリックしてボタンを離さずにマウスを移動すると、ボックス全体を動かすことができます。すでに選択されているボックスを移動させたいときには、まずボックスの外をクリックして選択状態を解除します。そうしないと、移動する代わりにテキストを選択してしまいます。
書き換えたテキストはオブジェクトの選択状態を解除しないと反映されません。オブジェクトボックスのテキストを書き換えると、実は古いオブジェクトは削除され、新しいオブジェクトが作られます。ということは、古いオブジェクトの内部状態は失われてしまいます。
2つのボックスをつなぐには、1つ目のボックスのアウトレットのどれかをクリックして、押したまま2つ目のボックスのインレットにドラッグしていき、ボタンを放します。実は目標のオブジェクトの中ならどこでもボタンを放していいんです。そしたら、もっとも近いインレットに接続されます。接続は接続をただクリックするだけで切断できます。(切断できるときには、マウスカーソルが"X"に変わります。)(訳注:Version 0.36以降ではクリックすると接続が青く変わりますので、消すにはbackspaceキーを押してください。)
上で述べたすべてのクリックは左マウスボタンでのことでした。代わって、右ボタンは"properties"と"help"のためのポップアップメニューを表示します。 Propertiesはナンバーボックスとグラフで使えます(そして将来、他のものにも同様に使えるようになるでしょう)。オブジェクトの上で"help"を選択すると、使い方を実演したPdパッチが表示されます。全体としてのキャンバスで(すべてのオブジェクトの外を右クリックして)"help"を選択すると、すべてのビルトインオブジェクトのリストが表示されます。
Control-q でPdを "quit(やめる)"しますが、確認されます。確認抜きでquitしたいときは、Control-shift-Qです。
Pdでは、オブジェクトはメッセージと/またはオーディオ信号で交信し合います。PdメッセージはMIDIメッセージやMusic Nシリーズの"Note cards"のように散発的です。
メッセージにはいくつかの引数が後ろに続く、セレクターが含まれています。セレクターとは、空白、セミコロン、コンマをいずれも含まず、非数字列としてパッチに現れる記号のことです。引数は記号か数字でしょう。Pdでの数字は32bit長の浮動少数なので、-8388608から8388608までの整数をちゃんと表現することができます。(Maxでは、整数型と浮動少数型にデータ型が分かれていますが、Pdでは浮動少数のみを使っています。)
メッセージが何かに届いたとき(この「何か」と言うのは多くの場合、ボックスのインレットなのですが、メッセージを受け取るものなら何でも良いのです。)、メッセージのセレクターがメッセージの受け手によってチェックされます。受け手がそのセレクターによってメッセージを認識すると、それに応じたアクションを実行します。例えば、ここに"float"オブジェクトがあります:
上にある2つの四角は普通、両方ともインレットと呼ばれていますが、左のインレットは自分に届いたメッセージを"float"オブジェクト自身に導きます。それに対して、右側のインレットはメッセージを付属のインレットオブジェクトに導きます。floatオブジェクトは適格-proper-(レフトハンドインレットと表現されます。)には"float"セレクターと"bang"セレクターのメッセージを受け取ります。ライトハンドインレットは"float"セレクターのメッセージのみを受け取ります。これらの2つのセレクターは、"symbol"セレクターと"list"セレクターも同様に、通常、オブジェクトの主要な行為-action-をなすものでした。そのおかげで、それが何であれ、最大限の柔軟性を持ったままオブジェクトを相互接続することができるのです。
数字で始まるメッセージを書くことは可能ですが、それをセレクターとして用いることはできません。数字が1つだけと言うメッセージには常に自動的に"float"セレクターがつけられます。そして、数字にその他の引数がつくメッセージには"list"セレクターがつけられます。
Pdでは、メッセージ送信が開始されるときいつでも、受け手は次々と奥にメッセージを送信します。そして、その次々と送信されたメッセージの受け手さらに奥にメッセージを送ります。こうして、それぞれのメッセージは結果として生じるメッセージ-consequent messages-の木を構成します。この木は深さ重視で実行されます。例えば、以下にパッチを挙げます:
この場合のメッセージの到着順はA-B-C-DまたはA-C-D-Bのどちらかです。メッセージ"C"はメッセージ"D"が終わらないと、終わりません。そして、メッセージ"A"は"A"、"B"、"C"、"D"すべてが終わったことにより、終わったこととなります。"B"と"C"のどちらが先に送信されるかは、区別ができません。これはあなたが接続をした順番によって変わります。(Maxでは順番は自動的に右から左に整理されます。)
メッセージの受け渡しはここに示すように無限ループを引き起こすこともできます:
ここで左側の"+"は右側の"+"が結果である"2"を受け取り終わって初めて終了できます。しかし、その右側の"+"は、左側の"+"が"3"を受け取り終わるまで終了できません。――以下同様。これが起こったら、Pdは「スタックオーバーフロー」を告げるエラーメッセージを出力するでしょう。
しかし、そのどこかに"delay"オブジェクトがあったら、ループを作ることは適切です。"delay"オブジェクトがメッセージを受け取ったら、(たとえdelay timeが0であったとしても)"delay"オブジェクトは将来にそれをスケジュールします。そしてまもなく「終了」します。Pdの内部スケジュラーはあとでその遅延メッセージを起こします。
少しの例外(特に"timer")を除き、オブジェクトは一番左のインレットを適切なインレットへのメッセージを出力されるメッセージとなれるという意味で"hot"(活性)であるとして扱います。 よって以下は合法的(そして妥当)なループ構成です:
ここで"f"というのは、 "float"の略語です。"+ 1"の出力が"f"の右側のインレットに接続されていることに注意してください。この"cold"なインレットは次に"f"が"bang"メッセージを送られるまで値を入れておくのみです。ひとつのオブジェクトの複数のインレットにメッセージを送ることはそのオブジェクトの行為を特定するのにしばしば望ましいことなのです。例えば、"+"で2つの数字を足すことができますが、正しく足すには、右側のインレットに先に値を与えなくてはなりません。さもないと、左側の値が着たときに"+"は(左のインレットは"hot"インレットなので)足し算を実行し、そして、この値は、右側のインレットにすでに入っているのが何であれ、それに足されるでしょう。
1つのアウトレットから(直接でも任意の長さのメッセージ受け渡しのつながりをとおってでも)1つのオブジェクトの違うインレットにつないだときに問題が発生します。この場合、どちらのインレットが先にメッセージを受け取るかがあいまいになります。たとえば"+"オブジェクトを数を2倍にするために使うことを考えてみてください。以下は不適切です:
ここで私は左のインレットを右側のそれをつなぐ前につなぎました。(パッチの見た目には現れませんが)上の"+"はこのようにして(左の)新しい後の入力を(右の)先の(訳注:古いという意味での先です)入力に足します。
"t"と省略される"trigger"オブジェクトは1つのアウトレットからの複数の接続を明確に決められた順番で分割するのに使えます。慣習により、Pdでのすべてのオブジェクトは、1つ以上のアウトレットからメッセージを送信するときに、それを右から左の順番で行います。 ワイヤ(訳注:パッチコード)をクロスさせることなく2番目のオブジェクトの複数のインレットにこれらを接続すると、その2番目のオブジェクトは一番左のインレットが最後にメッセージを受け取るようになります。これは通常あなたが望んでいることです。ここに先の例のあいまいさをなくす"trigger"の使い方を挙げます:
"Cold"な(つまり1番左ではない)インレットはほとんど例外なく1つの値を保持します(数値か記号のどちらかです)。"line"と"line~"は例外ですが、これらの値は"sticky"(訳注:粘着性または付箋紙的)です。すなわち、一度値をセットすると次にセットするまで値は有効です。("line"の例外はまともな理由によります)
実行順序についてたまにもう1つの問題がでてきます。それは2つのメッセージが1つの"cold"インレットに送られたときの順番です。この場合、メッセージはマージされる(訳注:くっつけられる)ので最後に届いた値が計算に用いられる値です。
メッセージボックスはあなたがメッセージを書き込むテキストボックスです。あるメッセージボックスがクリックされるか何かインレットに送られるかして活性化されると、1つ以上のメッセージがそのメッセージボックスのアウトレットか指定されたその他のどこかに送られます。
上の1番目のメッセージボックスは1つの数値、1.5を含みます。この1.5というメッセージは暗黙的に"float"セレクタを持ちます。2番目は3つの数値のリストです。そして、3番目において、セレクターは"my"で、2つの引数は数値5と記号"toes"です。
複数のメッセージはいかに示すようにコンマで区切ることができます:
ここで、3つのメッセージは数値1, 2, 3です。そして、それらは順順に送られます。("trigger"オブジェクトのように間を開けずに、そして深さ重視の因果法則なので、どんな"1"によって起こされるアクションも"2"によって起こされるあらゆることよりも先に生じ、――以下同様。)
セミコロンもメッセージを分けます。セミコロンの後に続くメッセージは送信先-destination-を表す記号を特定しなければなりません。(言い換えると、セミコロンは現在送信先-current destination-をクリアするので、次のメッセージは新しい現在送信先を決定するという点を除いては、コンマに似ています。) はじめ、現在送信先はメッセージボックスそれ自身ののアウトレットです。以下の例では、先導しているセミコロンはすぐにメッセージの送り先をアウトレットから"fred"と名づけられたreceiveオブジェクトに変更し、同様に次のメッセージは"sue"に送られます。
特定の他のオブジェクト(Pdウィンドウ、それとたとえば、配列)はPd名を持っており、この方法でメッセージを送ることができます。そして、スペシャルオブジェクト"pd"はDSPを開始したり停止したりすることができるように定義されています。
以下に示すように変数をメッセージボックスに置くことができます:
ほら、"$1"等(訳注:"$2"や"$3"その他もありうるという意味で「等」なのです)は 届いたメッセージを参照します。(そして、それらは"bang"メッセージを送るか、メッセージボックスをクリックして活性化するかを定義されていません。)ダラー印変数は届いたメッセージによって数値か記号になります。もし記号なら、変数をメッセージセレクタや送信先を特定することにだって使うことができます。
Pdを使ってオーディオパッチを作ることができます。オーディオパッチは楽音を合成したり、入力音声信号を解析したり、入力音声信号を変換したオーディオ出力を作るためにそれ処理したり、 オーディオ処理とほかのメディアを統合したりできます。この節ではPdがどのようにオーディオ信号を扱うかを説明します。
Pd's audio signals are internally kept as 32-bit floating point numbers, so you have all the dynamic range you could want. However, depending on your hardware, audio I/O is usually limited to 16 or 24 bits. Inputs all appear between the values of -1 and 1; and output values will be clipped to that range.
Pd assumes a sample rate of 44100 unless you override this in Pd's command line. Pd doesn't check that this matches the sample rate of audio input or output, nor does Pd attempt to set your computer's audio sample rate to its own. If the audio system is running at the wrong sample rate, audio output will be transposed (you can check this using the "test audio and MIDI" patch; see the help menu).
Pd can read or write samples to files either in 16-bit or 24-bit fixed point or in 32-bit floating point, in WAV, AIFF, or AU format, via the soundfiler, readsf, and writesf objects.
Inlets or outlets are configured in Pd either for messages or audio; it's an error to connect an audio outlet to a non-audio inlet or vice versa; usually these errors are detected at "sort time" when audio is started or the network changed with audio running. An object's leftmost inlet may accept both audio and messages; any other inlet is either one or the other. There is no quick way to tell whether an inlet or output is for audio or messages; consult the help window for the object.
The audio network, that is, the tilde objects and their interconnections, must be acyclic. If there are loops, you will see the error message at "sort time." When errors are reported at sort time there is no easy way to find the source of the error. You can build algorithms with feedback using nonlocal signal connections.
Your subpatches can have audio inlets and outlets via the inlet~ and outlet~ objects.
If you want to use a control value as a signal, you can use the sig~ object to convert it. The +~, -~, *~, /~, osc~, and phasor~ objects can be configured to take control or signal inputs.
The other direction, signal to control, requires that you specify at what moments you want the signal sampled. This is handled by the snapshot~ object, but you can also sample a signal with tabwrite~ and then get access it via tabread or tabread4 (note the missing tildes!). There are also analysis objects, the simplest of which is "env~", the envelope follower.
Larger block sizes than 64 should result in small increases in run-time efficiency. Also, the fft~ and related objects operate on blocks so that setting the block size also sets the number of FFT channels. You may wish to use block sizes smaller than 64 to gain finer resolutions of message/audio interaction, or to reduce "block delay" in feedback algorithms. At the (untested) extreme, setting the block size to one allows you to write your own recursive filters.
You can use switch~ to budget your DSP computations; for instance you might want to be able to switch between two synthesis algorithms. Put each algorithm in its own subpatch (which can have sub-sub patches in turn, for a voice bank for instance), and switch each one off as you switch the other one on. Beware of clicks; if you have a line~ controlling output level, give it time to ramp to zero before you switch it off or it will be stuck at a nonzero value for the next time it comes back on.
When a subpatch is switched off its audio outputs generate zeros; this costs a fairly small overhead; a cheaper way to get outputs is to use throw~ inside the switched module and catch~ outside it.
Send~ just saves a signal which may then be receive~d any number of times; but a receive~ can only pick up one send~ at a time (but you can switch between send~s if you want.)
Don't try to throw~ and catch~ or send~ and receive~ between windows with different block sizes. The only re-blocking mechanisms which are well tested are inlet~ and outlet~.
When you send a signal to a point that is earlier in the sorted list of tilde objects, the signal doesn't get there until the next cycle of DSP computation, one block later; so your signal will be delayed by one block (1.45 msec by default.) Delread~ and delwrite~ have this same restriction, but here the 1.45 msec figure gives the minimum attainable delay. For nonrecursive algorithms, a simple flanger for example, you might wish to ensure that your delread~ is sorted after your delwrite~. The only way to ensure this is to create the delread~ after you created the delwrite~; if things get out of whack, just delete and re-create the delread~.
In the intervals between, delays might time out or external conditions might arise (incoming MIDI, mouse clicks, or whatnot). These may cause a cascade of depth-first message passing; each such message cascade is completely run out before the next message or DSP tick is computed. Messages are never passed to objects during a DSP tick; the ticks are atomic and parameter changes sent to different objects in any given message cascade take effect simultaneously.
In the middle of a message cascade you may schedule another one at a delay of zero. This delayed cascade happens after the present cascade has finished, but at the same logical time.
The Pd scheduler maintains a (user-specified) lead on its computations; that is, it tries to keep ahead of real time by a small amount in order to be able to absorb unpredictable, momentary increases in computation time. This is specified using the "audiobuffer" or "frags" command line flags (see getting Pd to run ).
If Pd gets late with respect to real time, gaps (either occasional or frequent) will appear in both the input and output audio streams. On the other hand, disk strewaming objects will work correctly, so that you may use Pd as a batch program with soundfile input and/or output. The "-nogui" and "-send" startup flags are provided to aid in doing this.
Pd's "realtime" computations compete for CPU time with its own GUI, which runs as a separate process. A flow control mechanism will be provided someday to prevent this from causing trouble, but it is in any case wise to avoid having too much drawing going on while Pd is trying to make sound. If a subwindow is closed, Pd suspends sending the GUI update messages for it; but not so for miniaturized windows as of version 0.32. You should really close them when you aren't using them.
If a message cascade is started by an external event, a time tag is given it. These time tags are guaranteed to be consistent with the times at which timeouts are scheduled and DSP ticks are computed; i.e., time never decreases. (However, either Pd or a hardware driver may lie about the physical time an input arrives; this depends on the operating system.) "Timer" objects which meaure time intervals measure them in terms of the logical time stamps of the message cascades, so that timing a "delay" object always gives exactly the theoretical value. (There is, however, a "realtime" object that measures real time, with nondeterministic results.)
If two message cascades are scheduled for the same logical time, they are carried out in the order they were scheduled.
In an object box, as in a message box, the text specifies a message; but here the message is to be passed to Pd itself, once, and the message's effect is to create the object in question. When you open a file, all the objects created are created using their text as "creation messages." If you type a new message into an object box (or change it), the old object is destroyed and the message is used to create the new one.
The selector of the message (the first word in the message) is a selector which Pd interprets to mean which type of object to create. Any message arguments (called "creation arguments") are used to parametrize the object being created. Thus in "makenote 64 250" the selector "makenote" determines the class of object to create and the creation arguments 64 and 250 become the initial velocity and duration.
An exception is made for subpatches whose "state" is the configuration of the subpatch; as a special case, this configuration is restored when the patch is read from a file. Also, if you rename the subpatch, for instance typing "pd jane" instead of "pd spot," the contents of the patch are preserved and only the text in the object box and the window title of the subpatch are changed.
It is probably bad style to specify creation arguments ala "makenote 64 250" if you are going to override them later; this is confusing to anyone who tries to understand the patch.
Object boxes may have many different classes. The class is usually determined by the selector of the creation message, i.e., the first atom of the creation message which is usually a symbol.
Each class comes with a fixed collection of messages it may be sent. For esxample, the "float" or "f" object takes "bang" and "float." These messages are sent to "float" objects (objects whose class is float) via the leftmost, hot inlet. (The right inlet is a separate, auxiliary object.) Objects of class "float" respond to the message "bang" by outputting their current value, that is, by sending a "float" message to their outlet. They respond to "float" messages by setting their value and then outputting it.
Each other class (like "float") in Pd has its own protocol for responding to messages it is sent, and may take "float" and "bang" messages, or others in addition or instead of them.
Unless they arrange otherwise by defining a "list" method, objects respond to the "list" message by distributing the arguments of the message to their inlets, except for the first argument which is passed as a "float" or "symbol" message to the object proper.
Object boxes contain text wwhich forms a message to be sent to Pd to create and initialize the object. Here, $1, etc., are taken from the context in which the patch was loaded. When the patch is a new document or opened from a file the "$" variables are undefined. But if the patch is an abstraction (see the next section) they are taked from the abstractions' creation arguments.
Constructions such as "$1-x" are expanded by string concatentation. This is the mechanism for making local variables. In particular, $0 in an abstraction is a counter which is guaranteed to be unique to that abstraction, so sends and receives with names like "$0-bear" can be used as local send/receive pairs.
Note that the expansion of variables such as $0 and $1 only works at the beginning of the symbol; so, for instance, "rats-$1" will not be expanded. Occasionally you may want to have double or triple substitutions; this can be done one stage at a time by nesting abstractions (with each subpatch adding its own $-variable to the beginning of a symbol and passing that on as argument to a further anstraction.)
the box in the middle, if clicked on, opens the sub-patch shown here:
The contents of the subpatch are saved as part of the parent patch, in one file. If you make several copies of a subpatch you may change them individually.
The objects, "inlet,", "inlet~," "outlet," and "outlet~,", when put in a subpatch, create inlets and outlets for the object box containing the subpatch. This works equally for one-off subpatches and abstractions. The inlet~ and outlet~ versions create inlets and outlets for audio signals. You can't mix messages and audio in a subpatch inlet or outlet; they must be one or the other exclusively. Inlets and outlets appear on the invoking box in the same left-to-right order as they appear in the subpatch.
To make an abstraction, save a patch with a name such as "abstraction1.pd" and then invoke it as "abstraction1" in an object box:
Here we're invoking a separate file, "abstraction1.pd", which holds the patch shown here (the border is the same as for the subpatch above):
You may create many instances of "abstraction1" or invoke it from several different patches; and changing the contents of "abstraction1" will affect all invocations of it as they are created. An analogy from the "c" programming language is that one-off subpatches are like bracketed blocks of code and abstractions are like subroutines.
Abstractions are instantiated by typing the name of a patch (minus the ".pd" extension) into an object box. You may also type arguments; for instance if you have a file "my-abstraction.pd" you may type "my-abstraction 5" to set the variable $1 to 5. This is defined only for object boxes (not for messages) in the abstraction. (For message boxes, "$1", etc, have a different meaning as described above.) If you want to send a message with a $1 in the sense of a creation argument of an abstraction, you must generate it with an object box such as "float $1", "symbol $1", or perhaps "pack $1 $2", which may then be sent to a message box.
The corresponding feature in Max (both Opcode and Ircam) was the "#1" construct. In a Max abstraction, "#1", etc., are replaced by the creation argument. This has the disadvantage that you can't edit the abstraction as instantiated in the patch since the "#" variables are substituted. In Pd the "$" variables in object boxes are spelled literally as "$" variables so that it's meaningful to edit them from within their calling patch. On the Pd side, however, there is the disadvantage that it's confusing to have "$" expanded at a different time in an object box than in a message box. In an object box, the "$" argument is expanded at creation time, and in a message box, at message time.
where the patch "abstraction2.pd" contains:
Here, the number box in the abstraction shows up on the box that invoked the abstraction. The "graph on parent" flag is set in the abstraction (and is saved as part of the abstraction); to set it, open the "properties" dialog for the "abstraction2" canvas by right-clicking on any white space in the patch.
To open the subpatch, right click on the object and select "open". (On Macintoshes without a 2-button mouse, you can double-click in edit mode instead.) It doesn't work just to click on the object in run mode since clicks are sent to visible controls and/or arrays.
When the sub-patch is closed, all controls in it appear on the object instead; so the number box in the sub-patch in the example above is the same one as you see in the box. Only controls are made visible in this way
Arrays in Pd should be allocated (and possible read in from a file) before beginning to make sound, since memory allocation and disk operations may take long enough to cause audio buffer overruns or underruns. Pd provides two ways to define new arrays, as "graphs" and "tables". In either case the array has a pre-defined name and size (i.e., number of points). Elements of the array are stored as floating-point numbers, 4 bytes apiece
If you use an array to store a one-second sound at 44.1 kHz you will need 176 kilobytes, or a one-minute sound, 10.6 megabytes. To store a sound with two or more channels, use a separate array for each channel.
Arrays are also useful as transfer functions, for example for nonlinear distortion of an audio signal, or to map a control onto a synthesis parameter. In situations like this one typically uses much shorter arrays, of no more than a few hundered elements. They are also useful for storing measured spectra derived from the fft~ objects, and probably for many other uses.
Arrays usually appear within subpatches created to house them, whether in "graph on parent" form (so that you see them within a rectangle drawn on the containing patch), or as a regular subpatch (which you see as a text box.) In the "graph on parent" form, an array appears as shown:
Arrays are indexed from 0 to N-1 where N is the number of points in the array. You can read an array value using the tabread object:
Here we see that the third point of the array (index 2) has the value 0.4. To write into the array you can use the tabwrite object:
In this example, sending the message sets the third element to 0.5. (You may also send the two numbers to the two inlets separately.)
The two previous examples showed control operations to read and write from and to arrays. These may also be done using audio signals. For example, the patch below creates a 440 Hz. tone with "array1" as a waveform:
Here phasor~'s outputs a sawtooth wave, repeating 440 times per second, whose output range is from 0 to 1. The multiplier and adder adjust the range from 1 to 11, and then the values are used as indices for tabread4~, which is a 4-point interpolating table lookup module. (Much more detail is available in the audio example patches in the "pure documentation" series.)
To create a new array, select "array" from the "put" menu. Up will come a dialog window to set initial properties of the array. By default, a new graph is created to hold the array, but it may also be housed in the most recently created graph instead. Other properties may be specified there and/or changed later using the "properties" dialog.
If you select "properties" on an array in a graph, you two dialogs, one for the array and one for the graph. The array dialog looks like this:
You may use this to change the name and size, in addition to another property, "save contents". If "save contents" is selected, the array's values are stored in the containing patch; otherwise they're initialized to zero each time the patch is reloaded. If you intend to use arrays to store sounds, you will probably not wish to store them in the patch but as separate soundfiles. This will be more efficient, and you may also then use a sound editor to modify them outside Pd.
If you check "delete me" and then "OK", the array wil be deleted. This is an odd interface for deleting an object, and is only provided because Pd lacks a mechanism for selecting arrays (so that "cut" could serve).
The graph dialog (which also pops up) is shown here:
The X bounds initially range from 0 to the number of points in the table minus one (this is a good choice for arrays, although graphs holding other kinds of objects might require other X bounds.) The Y bounds should be chosen to reflect the natural range of the table, so that stored sounds would naturally range from -1 to 1, but a sequence of frequency values might range from 0 to 20,000. Finally, you choose the screen size of the graph, width and height, in screen pixels.
Many other operations are defined for arrays; see the related patches in the tutorial (starting at 2.control/15.array.pd) for more possibliities.
The original idea in developing Pd was to make a real-time computer music performance environment like Max, but somehow to include also a facility for making computer music scores with user-specifiable graphical representations. This idea has important precedents in Eric Lindemann's Animal and Bill Buxton's SSSP. An even earlier class of precedents lies in the rich variety of paper scores for electronic music before it became practical to offer a computer-based score editor. In this context, scores by Stockhausen ( Kontakte and Studie II) and Yuasa (Toward the Midnight Sun) come most prominently to mind, but also Xenakis's Mycenae-alpha, which, although it was realized using a computer, was scored on paper and only afterward laboriously transcribed into the computer.
Pd is designed to to offer an extremely unstructured environment for describing data structures and their graphical appearance. The underlying idea is to allow the user to display any kind of data he or she wants to, associating it in any way with the display. To accomplish this Pd introduces a graphical data structure, somewhat like a data structure out of the C programming language, but with a facility for attaching shapes and colors to the data, so that the user can visualize and/or edit it. The data itself can be edited from scratch or can be imported from files, generated algorithmically, or derived from analyses of incoming sounds or other data streams. Here is one simple example of a very short musical sketch realized using Pd:
The example, which only lasts a few seconds, is a polyphonic collection of time-varying noise bands. The graphical ``score" consists of six objects, each having a small grab point at left, a black shape to show dynamic, and a colored shape to show changing frequency and bandwidth. The horizontal axis represents time and the vertical axis, frequency (although, as explained later, this behavior isn't built into pd). The dynamic and frequency shapes aren't constrained to be connected or even to be proximate, but since they pertain to the same sound their horizontal positions line up. In this example the last (furthest-right) object is percussive (as seen by the black shape) and has a fixed frequency and bandwidth, whereas the large, articulated shape in the center has a complicated trajectory in both frequency and dynamic. The color of the frequency trace determines the voice number used to realize it.
Each object is thus composed of a combination of scalar values (color; aggregate position in X and Y coordinates) and array values (time/value pairs for the black traces and time/frequency/bandwidth triples for the colored ones.) This is all specified by the user using Pd's ``template" mechanism.
Here is the template associated with the graphical objects shown above:
Templates consist of a data structure definition (the "struct" object) and zero or more drawing instructions ("filledpolygon" and "plot"). The "struct" object gives the template the name, "template-toplevel." The data structure is defined to contain three floating point numbers named "x", "y", and "voiceno," and two arrays, one named "pitch" whose elements belong to another template named "template-pitch," and similarly for the array "amp."
In general, data structures are built from four data types: scalar floats and symbols, arrays (whose elements share another, specified template) and lists (whose elements may have a variety of templates). The contents of a Pd window themselves form a list. Pd's correlate of Max's "table" object is implemented as a top-level array whose elements are scalars containing a single floating-point number.
Data structures in Pd may nest arbitrarily deeply using the array and list types. For example, a collection of sinusoidal tracks from an analysis engine could be implemented as an array of arrays of (pitch, amplitude) pairs; this appears as example 12 in Pd's FFT object online tutorial.
After the "struct" object in the template shown above, the remaining three objects are drawing instructions , first for a rectangle ("filledpolygon"), and then for two arrays. The various graphical attributes that are specified for drawing instructions may be numerical constants or data structure field names; in the latter case the value varies depending on the data. For instance, the second creation argument to "plot" is the color. The first "plot" plots the "amp" field and the color is given as 0, or black. The second one plots "pitch" using the color "voiceno". In this way the color of the second trace is attached to the "voiceno" slot in the data structure, so that color will vary according to its "voiceno" slot.
Pd objects are provided to traverse lists and arrays, and to address elements of data structures for getting and setting. Here is a patch showing how these facilities could be used, for example, to sequence the graphical score shown above:
Pd has no built-in sequencer, nor even any notion that "x" values should be used as a time axis. (However, a "sort" function is provided, which reorders a list from left to right, on the assumption that users might often want to use Pd data collections as x-ordered sequences.) Recording sequences of events into lists, and/or playing the lists back as sequences, are functionalities that the user is expected to supply on top of Pd's offerings, which, it is hoped, would allow those functionalities within a much larger range of possibilities, to include random reorderings of events, score following, self-modifying scores, reactive improvisation, and perhaps much more.
Traversal of data is made possible by adding a new type of atom, "pointer", to the two previously defined types that make up messages, to wit, numbers and symbols. Unlike numbers and symbols, pointers have no printed form and thus can't be uttered in message boxes. Traversal objects such as "pointer" and "get" (among several others) can generate or use pointers. The pointer data type is also integrated into pipe-fitting objects such as "pack", "unpack", and "route".
In the patch shown above, the topmost "pointer" object holds a pointer to the next object to "play" (by sending it to one of the "voice" abstractions at bottom.) The pointer object takes a "traverse" message to set it to the head of the list (named "pd-data"), and "next" messages to move to (and output) the next datum in the list (i.e., the next in the list of six objects in the score). Another "pointer" object is also used, further down, as a storage cell for pointers just as "float" is for numbers.
The center of any sequencer is always the "delay" object, which must be fed the time difference between each event (including the non-event of hitting "start") and the next. As we extract each of the six objects in the score, we must wait the delay for playing that object, and then send its pointer to one of the "voice" abstractions to play it. However, we have to inspect the object itself to know the delay before playing it. So, in the loop, we peel off the first remaining object to play and inspect the time difference between it and the previous one, using this value to set the delay, but also storing the pointer in the lower "pointer" and "pack" objects.
The time difference needed to set the delay object is obtained using the "get template-toplevel x" object. (This is converted to incremental time ("-"), corrected for tempo, and fed to the delay.) Pd provides the "get" and "set" objects for reading and writing values from data structures. The two "get" objects shown here obtain the "x" and "voiceno" fields of the current object. The template name (template-toplevel) is supplied to the "get" objects so that they can look up the offset of the necessary field(s) in advance, for greater run-time efficiency.
Once the delay has expired, the object's pointer is recalled (the lower "pointer" object), and the voice number is recalled. This is packed with the pointer itself and routed, so that the pointer goes to the appropriate voice. The voice number is shown as the color of the frequency trace in "999" units (first digit red, second green, third blue) and the "route" is arbitrarily set up to select among the six primary and secondary colors plus black.
The details of extracting the pitch and dynamic breakpoints from the arrays defined in the template are managed in the "voice" abstraction. The "voice" abstraction receives a pointer to a given object and manages the sequencing of the arrays; so it contains two sequencers itself. The nesting of the overall structure of the sequencer patch mirrors the nesting of the original data structures. Finally, the voice abstraction puts its audio output on a summing bus.
More general patches can easily be constructed which access heterogeneous lists of objects (having different templates). In this way, an arbitrarily rich personal "score language" can be developed and sequenced.
In general, accessing or changing data is done via "pointers" to "scalars". Numbers and symbols withing scalars are accessed using the "get" object and changed, in the same way, using "set". Since lists and arrays are composed of scalars, every actual number or symbol in a data heap will be a number or symbol element of some scalar. To access them, it suffices to have objects to chase down elements of lists and arrays (given either a global name or a pointer to the containing scalar).
Lists are traversed in the way shown above; to get to a sublist of a scalar, the "get" object will provide a pointer, in the same way as it provides "float" or "symbol" elements of scalars. For arrays, an "element" object is provided which, given a scalar, a field name and a number, chases down the numbered, scalar, element of the named array field.
To alter "float" or "symbol" elements of scalars is straightforward using the "set" object, but arrays and lists can't be set by assignment; there is no suitable data type available withing messages. Lists could possibly be "settable" by passing pointers to other lists, but permitting this would have required either automatically doing deep copies of data structures to carry out the assignments, or else implementing a garbage collecting memory management system, either of which would be difficult to realize within real-time computation time constraints. Instead, all the data hanging from a scalar is considered as belonging to that scalar, and is left in memory until the scalar is deleted; the data may be changed atom by atom, but primitives are not provided which would imply unpredictable execution times.
The "getsize" and "setsize" objects are provided to access or change the number of elements in the array. For lists, an "append" object appends a new scalar for a given template to a list, after the element pointed to. (To insert a scalar at the beginning of a list, the pointer can be set to the "head" of the list, a formal location before the first list item.) Deletion is less flexible; the only operation is to delete an entire list. (There's no reason not to provide finer-grain deletion mechanisms except that it's not clear how to protect against stale pointers efficiently, except by voiding the entire collection of pointers into a list.)
The graphical score shown above can be edited by dragging breakpoints, or by adding and deleting them, using mouse clicks. Also, entire objects or collections of them may be copied, pasted, and dragged around the screen. Alternatively, there is an editable (or computer generate-able or parse-able) text representation for the data, which may be seen or changed in a dialog window or read and written to external text files.
Since the graphical presentation of data objects is determined by drawing instructions, the drawing instructions are interpreted backwards to alter data as a result of mouse operations. If a given graphical dimension is controlled by a variable, that variable is then controlled by dragging along that dimension; if the dimension is constant, it can't be altered by dragging.
Tricky situations can arise when the user changes the contents of templates. A change in drawing instructions can be accommodated by simply tracking down and redrawing all data objects using the template. However, changing the "struct" object itself make for less straightforward situations. The user might wish to reorder fields, delete them, add new ones, or rename them. When a "struct" object changes, Pd automatically conforms the data from the old structure to the new one. Fields with the same name as previously are maintained (reordering them as necessary); and if a field disappears but another of the same type appears, the new one(s) are taken to be renamings of the old one(s) in order of appearance. New fields which cannot be matched in this way with previously existing ones are assumed to be new and are initialized.
It can happen that two "struct" objects compete to define the same data structure, or that the user reads in data from a file which expects a different version of the structure, or alternatively, that the "struct" object for existing data objects disappears. For this reasn, Pd maintains a private representation of the last active version of a "struct" until all similarly named "structs," as well as all data using that "struct", have disappeared. If the user introduces a new version of the "struct" and only later deletes the "current" one, the data is only conformed to the new version once the old one is deleted. In this way we avoid getting into situations where data is left hanging without its structure definition, or where data ends up belonging to two or more structures of the same name. The worst that can happen is that data may lose their drawing instructions, in which case Pd supplies a simple default shape.
When examples get more complicated and/or dense than the one shown here, it becomes difficult to see and select specific features of a data collection; more work is needed to facilitate this. There should be some facility for turning drawing instructions on and off, or perhaps for switching between versions of a template, depending on the user's desired view. There should also be a callback facility in the template for when an object is edited with the mouse, so that the user can bind actions to mouse clicks.
More generally, the collection of traversal objects that Pd provides is adequate to support a variety of modes of data collection and use, such as analysis and sequencing. But the patches required to traverse the data collections are not always simple. It would be desirable to find a more straightforward mechanism than that provided by the "pointer", "get" and "set" objects.
The "data" facility, although part of the original plan for Pd, has only recently been implemented in its current form, and as (hopefully) the user base grows there will surely be occasions for many further extensions of the data handling primitives and the graphical presentation and editing functions.