GameOfLife / Unit-Lib

The Unit Library is a system that provides high level abstractions on top of the SuperCollider language.
25 stars 6 forks source link

enable/disable individual units from gui during playback #19

Open miguel-negrao opened 11 years ago

miguel-negrao commented 11 years ago

it would be nice to have a "power" icon for each unit in the gui which would enable/disable each unit, even while the chain is playing. When enabling the unit would start playing from the current position. This is very useful to quickly A/B with and without some effect, or to temporarily disable an effect, and is standard for plugins in DAWs.

woutersnoei commented 11 years ago

hmm, would not be safe for some units. I would say it could only work for units that do processing, not generating or output.

woutersnoei commented 11 years ago

We could have a flag in Udef that decides if it is allowed to disable, and show the button only for those?

woutersnoei commented 11 years ago

Picking up at score position can be quite tricky, since unit nor chain doesn't know it's parent score.

miguel-negrao commented 10 years ago

Each Udef could specify if it can be disabled or not. Indeed picking up at score position would be tricky, but this would still be useful for non score use.

woutersnoei commented 10 years ago

Yes sure. Perhaps we could call it 'bypass'. A bypassed state could also be stored as one of the args, so that it would save with the score. This would require some digg

woutersnoei commented 10 years ago

Digging.

woutersnoei commented 10 years ago

hmm not sure about this one. Haven't tested it, but I think it may be problematic when syncing multiple servers (Synth:run is async). Also I think it would be better if U itself handles with the run command internally, so that it is also possible to do it via code. The functionality is also limited to when the chain is actually playing. And it could be "dangerous" for the uninformed user as it doesn't make a distinction between units that can safely be paused and those that can't. For example if I would disable an output unit during playback, I would also have no way to stop the whole chain unless I first enable it again. In short, I think this feature is still a bit too experimental to put in main.

woutersnoei commented 10 years ago

Wouldn't it be a better idea to include a 'u_bypass' control in some of the UOut classes? Then the udef builder would be in control of the option. Also it wouldn't actually stop the processing, only bypass it. (or is the stop of processing the whole point?). The benefit of that would be that there are no sync problems, and there can be no arbitrary input on internal UChain buses (there would be if you would for example disable a soundfile playback synth at the start of a chain by pausing it; the unit not only outputs on the bus, it also clears everything that was in it before)

woutersnoei commented 10 years ago

Another pro of this approach is that we would be able to actually "save" the bypassed state.

miguel-negrao commented 10 years ago

Ok, I see your points. For my use case indeed I just need to pass through the audio from the previous chain (i.e. preview with and without a filter), I'm not actually trying to save cpu, so having a 'u_bypass' control would be fine with me. Perhaps we could reserve a private bus as a "garbage bus" and then just set the ReplaceOut to that bus when bypassed ? That seems to do the trick:

(
Udef(\test1, {
    Out.ar(0, WhiteNoise.ar * 0.1.dup )
});

Udef(\test2, {
    ReplaceOut.ar(\bus.kr(0), LPF.ar(In.ar(0,2), 400) )
});
)

(
x = UChain(\test1);
x.prepareAndStart
)

(
x = UChain(\test1, \test2);
x.prepareAndStart
)

x.units[1].set(\bus, 500)

If this looks like a good approach, I can look into implementing it.

woutersnoei commented 10 years ago

sounds a bit dirty :-). But there are still some problems with this approach; for example if the input bus is different from the output bus (which can happen). Or if the unit creates multiple outputs from a single input. We would then perhaps expect to get the input signal unaltered on the output bus. Or should the functionality be limited to units that always have the same input as output bus? (in which case we should have a way to force that)

woutersnoei commented 10 years ago

Perhaps we should have a special UOut variant with bypass option, where we provide the supposed bypass signal ourselves inside the udef?

miguel-negrao commented 10 years ago

What I would expect to happen when bypassing a unit is that it's as if it was never there in the first place. I was thinking that all UOuts have this built in (It's okay to define the same control multiple times, right ?) so when the bypass control is set on a unit (x.set(\u_bypass, 1) all UOuts will start writing to the garbage bus, which means they will not overwrite anything before that unit, which is then like if they never existed.If the output bus is different from the input bus or there are multiple outputs,then sure those multiple outputs will disappear, but that's what is to be expected if the unit was never there to start with, unless you mean something different by "bypassing".

I made a quick implementation which seems to work:

    *new1 { |selector = \ar, id = 0, input, offset = false|
        id = this.getControl( \kr, this.getControlName( 'o', selector, id ), "bus", id);
        if( offset ) {
            var bus = this.firstBusFor( selector ) + id;
            bus = Select.kr(\u_bypass.kr, [bus, 900/*garbage bus*/]);
            ReplaceOut.perform( selector, bus, Silent.ar );
            ^OffsetOut.perform( selector, bus.poll, input );
        } {
            var bus = this.firstBusFor( selector ) + id;
            bus = Select.kr(\u_bypass.kr, [bus, 900/*garbage bus*/]);
            ^ReplaceOut.perform( selector.poll, bus, input );
        };
    }

(
x = UChain(\test1, \test2, \stereoOutput);
x.prepareAndStart
)

x.units[1].set(\bus, 500)

(
Udef(\test1, {
    UOut.ar(0, WhiteNoise.ar * 0.1.dup )
});

Udef(\test2, {
    UOut.ar(0, LPF.ar( UIn.ar(0,2), 400) )
});
)

x.units[1].set(\u_bypass, 1)

Off course, I still would have make sure that the garbage bus can never be used for audio.

woutersnoei commented 10 years ago

hmm, interesting. When I try using a non-existent output bus in 3.5 it plays on the first bus instead. But in 3.6 it seems to vanish. WFSCollider is still stuck to 3.5 for now though, so the garbage bus trick won't work there. Unless it is an actually existing bus..

miguel-negrao commented 10 years ago

Yes, my idea would be that it is an existing bus, which is reserved for that function.

woutersnoei commented 10 years ago

In any case, if we would have a multi-channel out unit which introduces a new bus in the chain (for example duplicateChannel), and then we would bypass it it would allow arbitrary bus data to enter the chain. This could be prevented if we would internally deal with what exactly is passed on to the output when hitting "bypass". I'm thinking of a scheme where we store all UIn's temporarily in Udef during the synthdef build, and then let UOut use them as source for bypass. If no UIns are used, then no bypass is created, and if there are any, enabling bypass would route them straight to the output bus, instead of the processed signal. And then there could be some flags in UOut to influence this automatic process. The benefit of this approach is that bypassing the processing would still keep up the created routing. And the udef build system would inteligently know if a bypass option makes sense or not (no input -> no bypass).

woutersnoei commented 10 years ago

Also we might want special bypass cases for example for delays and similar, where we may want to keep the dry signal setting unaltered and only remove the delayed signal at bypass..

miguel-negrao commented 10 years ago

How would you deal with a situation with more outputs then inputs ?

var x = UIn.ar(0,4)

UOut.ar( 0, x.sum.dup(8) )

would you just send the inputs directly to outputs wrapped around when you run out of inputs ?I guess the reasoning would be that if you do that, it's an arbitrary assignment of inputs to outputs, but at least the content might be closer to what the next chain would be getting then if it was getting some audio from a previous unrelated unit.

woutersnoei commented 10 years ago

yes indeed situations like that it can still be unclear what to do exactly. In such occasions we may want to interfere with the automatic choice. Normally I would say though we can just wrap around the inputs directly. That way at least the units with the same number of inputs as outputs would still be transparent, and in other cases it would still be better than arbitrary data I guess.

A situation where the system I proposed comes in handy is when a filter unit is used to route a processed version of a signal to another output. I.E:

UChain( 'pulse', [ 'lowPass', [ 'u_o_ar_0_bus', 1 ] ], [ 'output', [ 'numChannels', 2 ] ] );

In this case, I suppose we would want the unprocessed signal to be on bus 0 and 1 when hitting 'bypass', rather than no signal or an arbitrary leftover from another chain on bus 1..

woutersnoei commented 10 years ago

Or, maybe we would want silence on bus 1 when hitting bypass? But then it would have to be guaranteed silence..

woutersnoei commented 10 years ago

so maybe it could work like this:

if one of the above is false, output silence if both true, output whatever came in on that bus (i.e. become transparent)

woutersnoei commented 10 years ago

and then a 'bypasSource' arg for UOut which could override the signal to be outputed on bypass.

woutersnoei commented 10 years ago

And for UMixOut the bypass could simply mute the output signal

miguel-negrao commented 10 years ago

so maybe it could work like this:

the UOut.ar looks into the currently defined controls to see if a corresponding UIn (with the same id) was created
if so, it checks if that is set to the same bus (this may change at any moment, so needs to be checked at control rate)

if one of the above is false, output silence if both true, output whatever came in on that bus (i.e. become transparent)

That sounds like a reasonable choice too. Shall I give it a try to implement it, or do you prefer to do it ?

ps: just discovered that selecting text in the github web interface and hitting 'r' will copy paste the text as quote. quite handy.

woutersnoei commented 10 years ago

yes please, give it a try, thanks!

miguel-negrao commented 10 years ago

Ok, it looks like I won't have time for this for quite a while, so I'll de-assign myself from the issue for the time being... sorry :-(