[sc] Environment, Eventなど

さて前回の続きということで、今回はこちらから。

The preceeding sections showed how to use Streams and Patterns to generate complex sequences of values for a single parameter at a time.

This section covers Environments and Events, which are used to build a symbolic event framework for patterns, allowing you to control all aspects of a composition using patterns.

この章からはEnvironmentとEventを使って、Patternを使ったコンポジションを操作する方法をやるそうです。
Environment、Eventは共に、Patternのための象徴的な(?)イベントフレームワークなんだそうです。

まずはEnvironmentから。

An Environment is an IdentityDictionary mapping Symbols to values. There is always one current Environment which is stored in the currentEnvironment class variable of class Object.

Environmentというのはそのまま環境と言っても良さそうです。
実際にはシンボルと値をマッピングするための辞書オブジェクト、という感じでしょうか。
IdentityDictionaryをなんて訳すべきかわかりませんが、、

また、Environemtnは常にcurrentEnvironmentクラスが保持しているようです。
つまり常にどこかのEnvironmentに属しているということみたいです。

Environmentのシンボルには

currentEnvironment.put(\myvariable, 888);
currentEnvironment.at(\myvariable).postln;

の様にアクセス出来るようですが、これは以下と全く等価です。

~myvariable = 888;
~myvariable.postln;

また、Environmentは自分でも作ることが可能なようです。

Environment has a class method make which can be used to create an Environment and fill it with values. What make does is temporarily replace the current Environment with a new one, call your function where you fill the Environment with values, then it replaces the previous current Environment and returns you the new one.

makeメソッドを使用することで新しいEnvironmentを作成し、また一時的にcurrentEnvironmentを置き換えるようです。

call your function〜以降がよくわかりません。
新しいEnvironmentで関数を呼んだら新しい環境で実行されますよ、ってぐらいの解釈でいいんですかね?全然意味がわかりません。読めません。
あー英語力が欲しい。が、とりあえずスルー。

(
var a;
a = Environment.make({
  ~a = 100;
  ~b = 200;
  ~c = 300;
});
a.postln;
)

こちらの結果は以下のようになりました。

Environment[ (a -> 100), (c -> 300), (b -> 200) ]

新しいEnvironmentに対するマッピングとして解釈されているようです。

使う時はuseメソッドを使います。

(
var a;
a = Environment.make({
  ~a = 10;
  ~b = 200;
  ~c = 3000;
});
a.use({
  ~a + ~b + ~c
}).postln;
)

結果は 3210 と出るので、こちらも正しく動作してます。

ただこれだけではただの名前空間だよな、と思ったのですが、以下のコードで諸々納得。

(
var f;
// define a function
f = { arg x, y, z; [x, y, z].postln; };
Environment.use({
  ~x = 7;
  ~y = 8;
  ~z = 9;

  f.valueEnvir(1, 2, 3); // all values supplied
  f.valueEnvir(1, 2); // z is looked up in the current Environment
  f.valueEnvir(1); // y and z are looked up in the current Environment
  f.valueEnvir; // all arguments are looked up in the current Environment
  f.valueEnvir(z: 1); // x and y are looked up in the current Environment
});
)

結果はこうです。

[ 1, 2, 3 ]
[ 1, 2, 9 ]
[ 1, 8, 9 ]
[ 7, 8, 9 ]
[ 7, 8, 1 ]

valueEnvir, valueArrayEnvirメソッドを使うと、渡されなかった引数と同名のシンボルをcurrent Environmentから探してきて、その値をアサインするようです。
Environment variableという言葉を使っても良ければ、仮引数と同名のEnvironment variableをcurrent Environmentから探して、その値をセットする、ということです。

前回意味がわからなかったコロンを使った値のアサインが出てきてますが、これはつまりキーワード引数みたいなものってことですね。なるほど。

ちなみに、fの仮引数zの定義をz=10とし、デフォルト値を与えてみましたが、結果は変わりませんでした。
どうやらEnvironment variableの方が優先されるみたいです。

また、宣言されていない変数aを仮引数にくっつけたらnilとなりました。
エラーとかにはなりませんでした。

サンプルコード見ると、同じインターフェイスを持った関数をランダムで選んで、valueEnvirしているようです。
これは何やらオブジェクト指向の継承あたりと似たものを感じます。
インターフェイスだけ知ってれば同じ値を渡せる、みたいな意味で。
お前オブジェクト指向の解釈間違ってるよ、、って声あれば是非ご指摘くださいませ、、
しかと受け止めさせて頂きます。

何にしてもこういう使い方なら確かに使えそうだなーと思いました。

 
次、Event。

The class Event is a subclass of Environment. Events are mappings of Symbols representing names of parameters for a musical event to their value. This lets you put any information you want into an event.

EventはEnvironmentのサブクラスで、musical eventの値の為のパラメータ名(シンボル)へのマッピングのことである、、??
さっきのEnvironmentの動作からなんとなく言いたいことはわかるんだけど、、うまい訳ができん、、

これの下の部分はEvent.defaultでデフォルトのEventがとれるって話をしてるのかな。
試しにEvent.defaultしてみたら(  )が返ってきた。これは、、合ってんのか?

The patterns discussed in parts 2 and 3 are known as “value patterns” because their streams return a single value for each call to next. Here we introduce “event patterns” which once turned into streams, return an Event for each call to next.

part2, 3で取り上げたPatternは、.nextを呼ぶごとに単一の値だけを返す”value patterns”で、ここで取り上げるのは.nextでEventを返す”event patterns”と。

The class Pbind provides a bridge between value patterns and event patterns. It binds symbols in each event to values obtained from a pattern. Pbind takes arguments in pairs, the first of a pair being a Symbol and the second being a value Pattern. Any object can act as a Pattern, so you can use constants as the pattern ( see \amp in the example below ).

Pbindクラスはvalue patternsとevent patternsの橋渡しをする。つまり、それぞれのイベントのシンボルとパターンから得られた値とを拘束する。
Pbindは引数をペアで取り、1つ目がシンボル、2つ目がvalue Pattern。
様々なオブジェクトがPatternとして使用出来、定数(つまり普通の数字?)をpatternと見なすことも可能。
というぐらいの訳で良いでしょうか。

Pbind( \freq, Pseq([440,880]) ).play

これを実行するとサイン波的なやつが2回鳴った。もちろん周波数はそれぞれ440, 880だったはず。

t = Pbind( \freq, Pseq([440,880]) ).asStream;
t.next(Event.default);
t.next(Event.default);
t.next(Event.default);

こちらを実行すると結果はこう。

( ‘freq’: 440 )
( ‘freq’: 880 )
nil

前回見たPatternと違うのはシンボルとペアになっている点。
これがミソなんだろうな。

他にはnextする際に引数にEventを与えている点。
Eventを渡さないとnilが返ってきた。なのでこれは必須な模様。
event patternsがそのEvent内でのシンボルと値(value pattern)のマッピングのことだ、というのを考えると納得。

asStreamで返ってくるオブジェクトはEventStreamPlayer。

EventStreamPlayer is a subclass of PauseStream. A PauseStream is just a wrapper for a stream allowing to play, stop, start it, etc…

EventStreamPlayerはPauseStreamのサブクラス。PauseStreamはストリームのラッパーで、play, stop, startなどを使えるようにする。

以下は英語難しいんでアレなんで原文貼り付けでスルー。サーセン。

EventStreamPlayers are initialized using the event stream returned by Pbind-asStream, as well as with a protoEvent. The EventStreamPlayer passes in a protoEvent, at each call to next on the Pbind stream. The Pbind stream copies the event to pass down and back up the tree of pattern streams so that each stream can modify it.

An EventStreamPlayer is itself a stream which returns scalars (numbers) which are used by the clock to schedule its next invocation. At every call to EventStreamPlayer-next by the clock, the player gets its delta values by querying the Event after it has been returned by the Pbind stream traversal.

 
まぁオブジェクトの詳細とか諸々分かんないけど概要は大体分かった。
サンプルコードと結果見ると分かり易い。

(
var pattern, stream;
pattern = Pbind(
  \abc, Prand([6, 7, 8, 9], inf ),
  \xyz, Pseq([1, 2, 3], 2 ),
  \uuu, 999 // a constant represents an infinite sequence of itself
);
stream = pattern.asStream;
7.do({ stream.next(Event.new).postln; });
)

結果は以下。

( ‘uuu’: 999, ‘abc’: 9, ‘xyz’: 1 )
( ‘uuu’: 999, ‘abc’: 9, ‘xyz’: 2 )
( ‘uuu’: 999, ‘abc’: 9, ‘xyz’: 3 )
( ‘uuu’: 999, ‘abc’: 8, ‘xyz’: 1 )
( ‘uuu’: 999, ‘abc’: 8, ‘xyz’: 2 )
( ‘uuu’: 999, ‘abc’: 8, ‘xyz’: 3 )
nil

なるほどなるほど。
数字も確かにPatternとして扱われてるみたいです。
ふむ。

次に以下のコードをコードを3回実行してみた。

(
var pattern, stream;
pattern =
  Prand([
    Pbind( \abc, Pseq([1, 2, 3])),
    Pbind( \def, Pseq([4, 5, 6])),
    Pbind( \xyz, Pseq([7, 8, 9]))
  ], 3);
stream = pattern.asStream;
10.do({ stream.next(Event.new).postln; });
)

— 1回目 —
( ‘abc’: 1 )
( ‘abc’: 2 )
( ‘abc’: 3 )
( ‘abc’: 1 )
( ‘abc’: 2 )
( ‘abc’: 3 )
( ‘def’: 4 )
( ‘def’: 5 )
( ‘def’: 6 )
nil

— 2回目 —
( ‘abc’: 1 )
( ‘abc’: 2 )
( ‘abc’: 3 )
( ‘xyz’: 7 )
( ‘xyz’: 8 )
( ‘xyz’: 9 )
( ‘xyz’: 7 )
( ‘xyz’: 8 )
( ‘xyz’: 9 )
nil

— 3回目 —
( ‘def’: 4 )
( ‘def’: 5 )
( ‘def’: 6 )
( ‘abc’: 1 )
( ‘abc’: 2 )
( ‘abc’: 3 )
( ‘abc’: 1 )
( ‘abc’: 2 )
( ‘abc’: 3 )
nil

ちなみに4回目実行したら全部選ばれました。

SynthDefした値とこれの値をくっつけて音変えて曲にする訳ですね。
んー、こういうアプローチはかなりアレコレ出来そうで面白い。

余談ですがEventってつまりPython的に言えばzip関数で2要素のタプルつくってそれを返すようなジェネレータ、ってことだよなー。
似たことPythonでやれる気もしてきた。

とりあえずPbindまでのざっくりの概要分かったので、あとは残りのドキュメントさらっと読んでサンプルコード見ながら手動かす感じでやってみる。
まぁ時間はかかりそうだけど、、

のらりくらりやってみます。

コメントを残す

メールアドレスが公開されることはありません。