Last active
March 7, 2020 22:42
-
-
Save eleses/8704f7abaea8d42c22b5eca527db5f48 to your computer and use it in GitHub Desktop.
A Pfset with closure
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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