… where \degree and \dur have been copied into the hardCodedEvent. In my specific case, I need it not to do that, and I’d prefer not to have to write e.g. Pfunc { (...) } in a live-coding context (where things need to “just work”).
Due to a more complicated case, I just inflooped and the whole system locked up (forced reboot) because I forgot about this detail of “embed”-ding events into events by way of a stream, and what was supposed to be a simple data structure ended up getting corrupted.
At this point, I don’t remember the reason I think this is for the case of e.g. Pseq([anEvent, anotherEvent]) where it does need to be merged with the inEvent – but within Pbind, it’s debatable, but just assumed to be desired in every case (i.e. a bit of a design problem?). I’ll beat my head against it again tomorrow but just thought I’d ask if there’s some way to bypass this behavior that I’ve overlooked.
Indeed – I totally forgot that Refs get dereferenced in streams – that’s very good, thanks! In the full context, I could actually set it up to wrap events in a Ref automatically.
I still think there’s a bit of a lingering question, about the circumstances where event merging makes sense or doesn’t make sense. But maybe that’s one of those SC4 questions (perhaps too risky to break compatibility).
Also this might strike one odd – if you inferred that the rule is to copy everything from the containing event into the inner event, it’s actually not that – would it be better here to copy in both a and b, or neither, or only the one? The actual behavior seems accidental.
I think it is understood that Pbind keys only know about their predecessors (your LambdaEnvironment was a nice whack at that) so not terrible that only the earlier keys are brought in… but I’m not sure why any keys should be put in!
I also thought it was that one, but it turns out it’s actually Event:next = next { arg inval; ^composeEvents(inval, this) } where composeEvents is then defined on Environment (?).
So perhaps composeEvents needs to incorporate the same logic:
Aaanyway… how I got into this mess was by bumping up against another assumption in the event API, namely that you can identify the relevant parameters by looking at the SynthDef being played. (That’s a totally separate issue, so I’m self-hijacking my own thread.)
My ddwPlug quark upends this by allowing new DSP logic to be introduced in a Syn(th)'s arg list:
… and now there’s a ‘rate’ parameter, undeclared in the original SynthDef.
Syn/Plug events also let you apply modulation to a parameter whose base value is specified separately:
// ffreq LFO is centered around 1000 the first time, 3000 the second
// without changing the Plug definition
(type: \syn, instrument: \test, ffreq: 1000, ffreqPlug: { |ffreq| Plug({ |ffreq, rate = 8| LFDNoise3.kr(rate).exprange(1/3, 3.0) * ffreq }, [ffreq: ffreq]) }, sustain: 3).play;
(type: \syn, instrument: \test, ffreq: 3000, ffreqPlug: { |ffreq| Plug({ |ffreq, rate = 8| LFDNoise3.kr(rate).exprange(1/3, 3.0) * ffreq }, [ffreq: ffreq]) }, sustain: 3).play;
… which works here because ffreq is known, from the SynthDef. But rate is not known this way, so if you wanted to specify \rate in a Pbind and modulate it with a ratePlug pair, no dice.
So I put in a forceArgs event parameter for Voicer-style events, which (for a related use case) is a dictionary. So that’s why I was putting (something: value) into a Pbind.
All of this points to perhaps needing a rethink about how to get parameters out of an event… not gonna happen today, but useful to think out loud about it.
Yes, good that you thnk through this again. We may need to fix it indeed. I also think that some of your innovations in the quarks could go into common in sc 3.15, at least if you think they are ready.
This is probably clear, but just for the motivation of the putAll, it is there to make it possible to have a stream of events as values:
a = Pseq([(note:2, dur:0.1), (note:7, dur: 0.2)], 2);
a.asStream.nextN(5, Event.default);
There are a couple of things to do. One is to fold Plug into Syn; I have some pondering about the interface to do, but there’s too much code duplication currently. The other is to streamline the \xxxPlug workflow, which is too verbose currently (where arg names have to be specified in the \xxxPlug function and in a Plug arg list and also, if the Plug is based on a synth function rather than a SynthDef name, as args to the synth function as well. It “works” but there’s got to be a tighter way to express it. Maybe a constructor class rather than a constructor function.
The challenge for the additional-args problem is that you need the full list of args to make the Syn object, but that arg list may not be known ahead of time. Sticky problem. A good summer-holiday dev project but I’m not sure I can finish it all very quickly.