TL;DR the best solution to this is to create a group, put all of the pattern’s nodes in the group, and then when stopping, you can do theGroup.release or theGroup.set(\gate, 0).
This gets into one of my favorite topics.
In the help, usage patterns like p = Pbind(...).play appear all over the place.
This leads to an assumption that Pbind is a general-purpose note-manager. (The concern about release behavior here stems from this assumption.)
I think a better object model for this is that Pbind should be one component of a general-purpose note-manager.
Think about what you need for a note-player.
-
Mixing: a bus to isolate this pattern’s resulting signal, and a fader synth for volume/pan control.
-
Order of execution: a group, with a specific relationship to other nodes.
Neither of these is directly involved in pattern evaluation. The usual object-oriented concern about encapsulation means that we need an object that just evaluates pattern data. That is Pbind. We already have it. We shouldn’t change that concept because it’s an essential part of a sensible object model. Adding generalized note-manager functions onto patterns themselves is to create a “god object” (an anti-pattern).
I would suggest a “wrapper around” a pattern that includes these concepts.
In my chucklib quark, I do it like this:
(
BP(\x).free; // start from scratch
Proto {
~prep = {
~group = Group.new;
};
~freeCleanup = {
~group.free;
};
~asPattern = {
Pbind(
\degree, Pwhite(-7, 7, inf),
\dur, 0.5,
\sustain, 3,
\group, ~group
)
};
~stopCleanup = {
~group.release;
};
} => BP(\x);
)
BP(\x).play;
BP(\x).stop;
The BP is a wrapper object that manages the behavior pattern, along with any resources that it needs. ~stopCleanup is for stop-time behavior.
Pmono sets up cleanup while Pbind doesn’t.
The default \note event type schedules the release at a time in the future that can be calculated right now.
In Pmono, the release time of the event that is playing right now is unknown. You have no idea how many events, or how much time, will elapse between now and release. That’s why you need the cleanup.
PmonoArticStream does something rather complicated, removing the cleanup in the sustain<delta (i.e. legato<1) notes. I’m honestly not sure why it is necessary for it do this, i.e. why can’t it leave the cleanup on for all notes
If legato < 1, then the release time is known (you’re definitely going to release before the next note, and the time of the next note is known from theEvent.delta). So it’s appropriate to scheduled for release in the future. It doesn’t make sense to release the same note twice, so it should be removed from the cleanup at that time.
This leads to a problem with the solution that I proposed above: you can’t cancel the scheduled releases – but the note has been released by ~stopCleanup, so the server will post failure messages about that. They are harmless (the goal was to remove a node; removing a node that was already removed means you end up in the state that you wanted anyway), but unnerving. (There is a way to suppress the messages.)
I’d also note that your use case is to stop the notes immediately on .stop, but I very often play overlapping long-swell notes (slow attack and release). In a performance, it would sound silly if the texture did not fade naturally to silence. So I prefer Pbind’s behavior as it is.
hjh