SuperDirt
SuperDirt copied to clipboard
Mutating events -- beyond the trigger message
With upcoming changes, Tidal can now represent effect changes beyond trigger messages.
For example sound "bev" # crush "1 2"
used to result in the second value of crush being discarded, but now (in the master branch but not released) it's preserved as applying to the second half of the event:
(0>½)-1|crush: 1.0f, s: "bev"
0-(½>1)|crush: 2.0f, s: "bev"
I'm still not sure how to reliably attach an identifier to an event, but if it could be done, how hard would it be to change an effect value on a running sample or synth?
Well I guess we could simply identify events with a combination of start time, end time, sound, and note/sample number - adding a note/sample number delta control for pitch bend / sample mashing
I think on the SC end, if you can identify which synth to send the message to it's fairly straightforward to update a parameter. But Tidal would have to send an identifier - I don't think even begin/end/s/n are enough to distinguish synths, you might have superimpose (# shape 0.4) $ s "sn"
. I think ideally Tidal would send a unique ID with each OSC bundle. Then the tricky part for SC is keeping a dictionary of Tidal ID -> Synth and maybe checking if things are still playing or not.
Yes I think begin/end/s/n only take us 80-90% of the way to identifying individual notes, but is better than nothing, and already better than MIDI. I think a truly unique ID would not be possible with the current representation, but it makes my head hurt to think about it.
Maybe @telephon has ideas about how this can work on the SC side, I remember we've discussed this sort of thing before.
Tidal master will now send an alphanumeric 'id' parameter with every message, if the cSendParts
config is set true.
I have a proof of concept for this working in classic dirt pretty well, e.g. this creates a nice vocal burbling: d1 $ sound "arpy" # vowel "[a e i o]*16" # speed 0.1
The only problem is that the part is sent every frame (20Hz by default) or more, even if no value changes. With a lot of sounds being sent, this will result in quite a lot of string comparison..
The main issue I see is that tidal doesn't (need to) tell sclang when a note is released, does it? If we need to keep track of ids, we also need to get rid of them, as @bgold-cosmos said.
There is a trick similar to the one in cutgroups, which allows us to make the reception of a parameter depend on a key-lock match. So you can send the change to all the synths at once (that is to the group), but only the one with a matching locks will respond.
But this means that we have to wrap every parameter into such a behavior, which may be a bit unwieldy for those who a used to the common syntax:
// instead of a function ~lockParam we'd have a class like `LockParam(…)`.
~lockParam = { |name, defaultValue|
Latch.kr(name.kr(defaultValue), BinaryOpUGen('==', \paramLock.ir, \paramKey.tr));
};
SynthDef("dirt_hpf" ++ numChannels, { |out|
var signal = In.ar(out, numChannels);
var hcutoff = ~lockParam.(hcutoff, 440);
var hresonance = ~lockParam.(hresonance, 0);
signal = RHPF.ar(signal, hcutoff.abs, hresonance.linexp(0, 1, 1, 0.001));
ReplaceOut.ar(out, signal)
}, [\ir, \ir, \ir]).add;
The trick is this:
(
(
SynthDef(\lockTest, { |param|
var value = Latch.kr(param, BinaryOpUGen('==', \paramLock.ir, \paramKey.tr));
value.poll(2);
}).add
)
x = Synth(\lockTest, [\param, 77, \paramLock, 1999, \paramKey, 1999]);
x.set(\param, 100, \paramKey, 7); // wrong key, no change
x.set(\param, 100, \paramKey, 1999); // correct key, this changes
x.set(\param, 20, \paramKey, 199); // wrong key, no change
Would this whole thing maybe be a way to get rid of global effects altogether?
The main issue I see is that tidal doesn't (need to) tell sclang when a note is released, does it? If we need to keep track of ids, we also need to get rid of them, as @bgold-cosmos said.
In classic dirt, I just ignored control (triggerless) messages that didn't match with any running sound.
yes, but you kept track of running sound processes which superdirt doesn't ...
This seems good to me @telephon, if you don't mind an experiment..
Currently I'm sending quite a long string for 'id' that is basically the identifying characteristics of an event separated by dashes. I can send a bool value to indicate whether it's an event onset or not as an additional parameter?
all we'd need is a number that tells us which synth is addressed. For each synth that is started, we also need this number.