-
Notifications
You must be signed in to change notification settings - Fork 79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Controlling parameters between events via parameter buses #190
Comments
good idea. |
thinking again about this: what would smooth mean here? |
One way this could work is that the panning runs one event or one cycle behind so that it knows where to interpolate to. Thoughts? And, maybe smooth isn't a useful descriptor and |
ah I see, so you want a pan that moves within each event. It would be possible to add a In principle, this would be a possible desire to have for every existing parameter, so I'm not sure if it would be better to introduce some architecture for such a thing. |
Yes, and that's a better suggestion.
I completely agree, and I think this would overcome one of the main limitations of the Tidal-SuperDirt relationship cc @yaxu. Should we create a separate issue for that? |
We could rename this one. What would we call it. "Parameter envelopes?" A simple case would be a list of values, equally spaced over the duration of an event (one at start one at end) that supercollider moves between linearly. We already use The interface for that simple case could then be |
I was thinking slightly more generally "parameter interpolation". It's going to give parameters more independence from the main pattern structure, so there should be a clear way to use the regular approach still. A notation for this would be great, but maybe we also want a way of doing this with an explicit parameter as well? It's always great having multiple ways of doing things, and sometimes the pattern notation can get quite dense. What would |
Yes I was just trying to start with the simplest case of linear interpolation,
A helper function could turn 'monophonic' patterns into this kind of envelope e.g. We should probably start with finding out how this kind of thing is usually done with supercollider, then looking for a minimal and expressive Tidal UI to support that. |
By the way I just checked and dirt used to have a pan_to parameter a couple of rewrites ago, back in 2011 :) http://slab.org/datadirt/datadirt_1.0.0.tar.gz |
I see, although I'm not sure
All that being said, I'm afraid I don't have any better ideas off the top of my head. Until we know a bit more how it's looking on SuperDirt side it might be easier to prototype with additional explicit parameters, and then come back to the UI/notation side later. And that is a great archeological find - no new ideas under the sun! |
My thinking is that both are lists, so should maybe make use of the same syntax, with wider context giving meaning to what the list is of. Additional parameters will likely be needed too, for giving the type of envelope |
Ok, so in this case the "lists" would be lists of patterns that describe interpolation points for the same parameter, with the first list being the "from" pattern, and subsequent lists being "to" patterns? Wouldn't your example though need to be |
tidalcycles/Tidal#397 related.. |
isn't interpolation just a special case? What you want, in a way, is to be able to change parameters on a running synth, which does the intrpolation internally, e.g. by lagging the parameter. |
Let's assume we have a synth event called "set", defined like this (we'll need a more general solution): ~dirt.soundLibrary.addSynth(\set, (play: { ~group.set(\freq, ~freq.value) })) Then in tidal, we could make a synth that has a long legato, and while it runs, set it: sound "supergong set set set" # freq "200 700 100 500" # legato 4 (I realise that it would be nice to have a legato that is relative to the cycle length) The synth supergong has no interpolation in its values, but this could work. Still need to test. Would this lead to a solution in your direction? |
That would only work for monophonic synths, unless we made progress on #133. My suggestion is to send a parameter as an array at trigger time, that sets an envelope on that parameter. |
I had a look at the OSCv1.1 spec and couldn't see support for lists |
The suggestion above is independent of all this. You can set the orbit's group, and this will set the parameters of all synths in that orbit. So (I thinkt at least) independently of this:
etc. |
I see. You might still want different sounds in the same orbit with different frequencies. I couldn't get the example to work, I get "Instrument isn't found" for "set" |
I think it should be: ~dirt.soundLibrary.addSynth(\set, (play: { ~synthGroup.set(\freq, ~freq.value) })) |
Hm I'm still getting the same:
If I run the addSynth line again I get
So it seems it's definitely adding it.. Strange. |
yes, I had the same. I need to investigate why this happens. This will be a good first step. If we want to separate the synths, we can think how to do this. There is still the possibility to allocate groups and put synths in groups. Alll this adds complexity though. |
Here is a first example that actually works: (
~dirt.soundLibrary.addSynth(\set,
(play: { |dirtEvent|
var group = dirtEvent.orbit.group.asGroup;
group.set(\freq, ~freq.value)
})
);
SynthDef(\supertest, {|out, freq=440, sustain=1, pan |
var sound = RLPF.ar(Pulse.ar(freq.lag(1)), (freq * 3).lag(2), 0.05);
Out.ar(out, DirtPan.ar(sound, ~dirt.numChannels, pan))
}).add
);
Then you can write: sound "supertest set set set" # freq "200 700 100 500" # legato 4 |
What we could do is add control routing busses to superdirt (we already have audio routing busses): (
~dirt.addModule(\get, { |orbit|
var bus = orbit.dirt.controlBusses.wrapAt(~fromBus);
orbit.server.sendMsg("/n_map", ~setParam, bus.index);
}, { ~fromBus.notNil })
)
(
~dirt.addModule(\set, { |orbit|
var bus = orbit.dirt.controlBusses.wrapAt(~setBus);
bus.set(~toValue);
}, { ~setBus.notNil })
) And then we could write in tidal, after having defined the right parameter functions:
Both parts, the Currently, we can do this already with audio signal routing, but not with setting controls. |
P.S. I've added controlBusses now, so the above code should almost work. let fromBus = pI "fromBus"
setBus = pI "setBus"
toValue = pF "toValue"
setParam = pS "setParam" |
OK, here we go: let fromBus = pI "fromBus"
setBus = pI "setBus"
toValue = pF "toValue"
setParam = pS "setParam"
fadeTime = pF "fadeTime"
d1 $ stack [
sound "supertest" # sustain 4 # fadeTime 1 # setParam "freq" # fromBus "7" ,
sound "supersilent*4" # setBus "7" # toValue "403 508 201 703"
] SuperDirt: (
SynthDef(\supertest, {|out, freq=440, sustain=1, pan |
var sound;
sound = RLPF.ar(Pulse.ar(freq.lag(1)), (freq * 3).lag(2), 0.05);
Out.ar(out, DirtPan.ar(sound, ~dirt.numChannels, pan))
}).add;
SynthDef(\supersilent, {|out, freq=440, sustain=1, pan |
var sound;
sound = Silent.ar(~dirt.numChannels);
Out.ar(out, DirtPan.ar(sound, ~dirt.numChannels, pan))
}).add;
~dirt.addModule(\get, { |event|
var bus = event.orbit.dirt.controlBusses.wrapAt(~fromBus);
event.orbit.server.sendMsg("/n_map", ~synthGroup, ~setParam, bus.index);
}, { ~fromBus.notNil });
~dirt.addModule(\set, { |event|
var bus = event.orbit.dirt.controlBusses.wrapAt(~setBus);
bus.set(~toValue);
}, { ~setBus.notNil })
) Does this go in the right direction? |
Instead of sending
... could tidal instead send the bus value with the name prefixed e.g. by '_'?
Then there could be multiple things set in one message
Instead of using '/play2' for setting a bus value, tidal could use a different OSC path, e.g.
|
This would work, but would incur a bit of calculation cost on all messages (we need to check every argument name for the prefix, but there is a primitive that is quite efficient). If this is ok, the nicest way would be:
But maybe numbers are just fine as well. Assuming that we do A very direct way would be to send:
We don't have to change anything on the superdirt side for that really. (Note that this won't work for all parameters, e.g. But: Then we need to guarantee that superdirt has the relevant bus numbers. That probably means that tidal needs to know the busses that superdirt has, and also how many. Superdirt could send them to tidal. |
The possibility of having synths playing out into audio busses which can then be mapped to arguments is already implemented, but this works differently (there, you mostly don't want to have the same pattern write and read). |
Did you mean a different link? |
If you wanted, you could also ask for each bus separately from dirt via OSC. Then there is no need for writing an allocator. |
Thanks! I guess a network ping pong each time a bus is used would slow things down though? |
You'd need it only the first time you define a pattern that uses the bus. But maybe yes.
I also think that strings are not necessary. Numbers are fine, if there is an implicit number allocator. |
Not sure if we need it at all, but you could use |
I got a bit bogged down in the technicalities of this, so keen to get a simple version released.. @telephon by Or is this something on the SuperCollider side? Anyway to reiterate, a simple start would be to support something like this to allocate a bus (denoted by the suffix
That reaches the scheduler as the identifier I think that's enough for now. It might be nice to be able to pattern the bus ids, so that one parameter could switch between receiving values from different busses, but that mgiht be taking it too far.. |
I think it is better if tidal can maintain the binding between names and bus indices, unless that's really too much work. Otherwise, superdirt can also do this, as long as tidal makes sure the timing is correct and names are marked as freed after use somehow.
So this is now for writing the bus signal, right? Then yes, since the synth lives across the messages it will keep the mapping. But this is just a matter of optimisation. For the synth that reads the bus signal, you'll have to send a I hope I understood you correctly. |
Yes just a matter of optimisation, but currently without this tidal will generate a lot of spurious identical bus messages at 20hz. Mainly I was just reiterating things after returning to this after a break. Though this turns out to be difficult:
Because of the book keeping needed to support multiple versions of the same parameter e.g.:
I think they have to have unique identifiers, so I might go back to using 'dial'. I find this fiddly though..
So perhaps something like this is better
That doubles the size of Params.hs.. Or I think we could use template haskell to generate the declarations and make it much smaller than it is. |
The explicit version (dial) is a good intermediate step, maybe. To go further, it seems that you need a way to identify the start and end of a particular subpattern? Then the ids for the dials could be hidden under a hood. And they could be overridden as well to make cross pattern communication possible. |
Ok I'm playing around with this for the first time, e.g.:
This sounds nice but there's seems to be a strange lag. If I comment out the ampbus line for example, it takes a couple of seconds for it to fade to 1, and then if I uncomment it, it similar amount of time to fully take effect.. |
Maybe let's reconsider what happens on the OSC level? What are the messages that are sent with |
A strange thing is that if I play this:
Then change the pan to 0, I hear the sound panning from one side to the other, over a couple of seconds. So there's a weird lag on all the effects if I use the bus controls on one of them. |
I would have said that But what you describe doesn't quite fit into this image. |
For debugging, you could use a synth and a special parameter: SynthDef(\debug, { |testvalue|
testvalue.poll(Impulse.kr(3));
}).add d1 $ sound "debug" # testvaluebus "2000 1000 500" |
I'm afraid I have been rather stupid. 'sax' is a long sound, and I forgot to add a 'cut' or 'legato' parameter. The amp cutting the sound for everything in sync was confusing me, so I didn't realise that different instances of the sound were overlapping. |
Playing with |
|
|
Just reading through the discussion to see what was lost in this simple implementation.. Maybe not too much, people have to handle allocating bus numbers by hand but that's OK I think. Currently if Tidal doesn't manage to handshake straight away (i.e. if superdirt is started after tidal) then it just sends the bus ids that the user specifies, without using a mapping sent from SuperDirt. I think this works as long as superdirt is the only thing running in supercollider? The bus allocations I see are always contiguous from 0 in any case. However it would be best to have a successful handshake in the usual cases:
The first two already work in Tidal 1.7. The third I've got working locally, with a simple loop waiting for a handshake. The fourth case would be solved by this:
i.e. a restarted superdirt receives a Looking at the source and behaviour though the |
@telephon is |
No, |
E.g.:
When
# pan "1"
is sent,# panmode
sets interpolation tosmooth
over1
cycle duration.The text was updated successfully, but these errors were encountered: