Right, and IMO it would be cleaner to have all the calculation in the Pbind, rather than dividing up the note and words calculations into different places.
It’s a little awkward with a drawFunc
that you can’t pass any arguments into it. Passing arguments is the normal way to run an action, where the action depends on some information provided at runtime. But that isn’t supported for drawFunc
, so we need another way.
I often/usually find it helpful to think of a GUI as displaying or updating the state of some other object – actually, this is relevant for most GUI situations, but it’s a nice way out of the no-argument-passing limitation here.
Instead of “Pbind → GUI,” think of:
- Pbind → storage object
- GUI ← storage object
We often use Pdefn as a storage object (see any number of threads about dynamically updating the contents of a Pbind).
(
Pdefn(\word, "nothing yet");
Pdefn(\color, Color.rand);
// these streams aren't "calculation" streams --
// only a convenient way to get the current value from the Pdefn-as-storage object
~wordStream = Pdefn(\word).asStream;
~colorStream = Pdefn(\color).asStream;
w = Window(" ", Rect(0, 0, 444, 444));
w.front;
// for convenience:
w.onClose = { ~graf.stop };
r = Rect(0, 0, 444, 444);
w.drawFunc = {
~wordStream.next.drawCenteredIn(r);
Pen.strokeRect(r);
w.view.background_(~colorStream.next);
};
~graf = Pbind(
\dur, 1,
\midinote, Pwhite(48, 72, inf),
\word, Pindex(
["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"],
Pkey(\midinote) % 12
),
\color, Pfunc { Color( * Array.fill(3, { rrand(0.6, 1) })) },
\xyx, Pfunc { |ev|
// put the things into storage
Pdefn(\word, ev[\word]);
Pdefn(\color, ev[\color]);
// then update the window
{ w.refresh }.defer(s.latency);
}
).play;
)
hjh