Skip to content

Instantly share code, notes, and snippets.

@eleses
Last active March 7, 2020 22:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eleses/8704f7abaea8d42c22b5eca527db5f48 to your computer and use it in GitHub Desktop.
Save eleses/8704f7abaea8d42c22b5eca527db5f48 to your computer and use it in GitHub Desktop.
A Pfset with closure
p = Pbind(\dur, Pseq([1, 2, inf]));
(r = PfsetC({var grp = Group(); ~group = grp;
{ grp.release.postln; fork { 3.wait; grp.free }}}, p).play);
r.reset; // /hit a few time before it reaches the infinite event
r.stop; // several groups freed.
/*
PfsetC is a Pfset with closure. Instead of passing in an additional function
for cleanup, the cleanup function is whatever the make/init function
*returns*. In contrast, this returned value is just ignored by plain Pfset. My
modification allows closure capture of "stuff" (resources) created by the
init/make function in init/make function-level variables, and so side steps
the mess of having to set up some other communication method between the
make/init and cleanup functions which is otherwise open to quite a few race
conditions if one is not very careful. For example, resetting the stream
before it finishes calls the make/init function twice in a row, without the
cleanup function being called in between (that's the standard
EventStreamPlayer reset behavior). If one creates a closure for every
init/make invocation, which happens automatically with PfsetC then the "right"
cleanup is called for each init automatically, as long as you "save" (a
handle) to the resources created in a init/make function-level variable.
Prototypical usage idea
PfsetC({
var saved = makemyresouce();
~useresource = saved; // pass to event
{ cleanunpmyresource(saved) } // cleanup function
}, pattern)
It's essential the last statement (return value) of the init/make function be
itself a function, so its execution is actually deferred to cleanup
time!
Concrete example, clean up a group by first releasing "softly" it
and then freeing after a bit more of time.
p = Pbind(\dur, Pseq([1, 2, inf]));
(r = PfsetC({var grp = Group(); ~group = grp;
{ grp.release.postln; fork { 3.wait; grp.free }}}, p).play);
r.reset; // /hit a few time before it reaches the infinite event
r.stop; // several groups freed.
*/
PfsetC : FuncFilterPattern {
//making cleanupFunc a member var would be a mistake; stream resets would overwrite it before it calling it!
*new { |func, pattern|
^super.new(func, pattern)
}
embedInStream { arg inevent;
var event, cleanup = EventStreamCleanup.new;
var cleanupFunc, envir = Event.make({ cleanupFunc = func.value() });
var stream = pattern.asStream;
var once = true;
loop {
inevent.putAll(envir);
event = stream.next(inevent);
if(once) {
cleanup.addFunction(event, { |flag|
envir.use({ cleanupFunc.value(flag) });
});
once = false;
};
if (event.isNil) {
^cleanup.exit(inevent)
} {
cleanup.update(event);
};
inevent = yield(event);
if(inevent.isNil) { ^cleanup.exit(event) }
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment