Skip to content
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

EnvGen still behaves incorrectly for early gate, but other doneActions #2189

Closed
telephon opened this issue Jun 15, 2016 · 13 comments
Closed

EnvGen still behaves incorrectly for early gate, but other doneActions #2189

telephon opened this issue Jun 15, 2016 · 13 comments

Comments

@telephon
Copy link
Member

@telephon telephon commented Jun 15, 2016

This is related to #1063.

// for EnvGen when called within a block after start,
// which doneAction works?
0 true
1 true
2 true
3 false
4 true
5 false
6 true
7 false
8 true
9 true
10 false
11 true
12 true
13 false
14 true

Here is a reproducer:


(


SynthDef(\test_env_done_actions, { |doneAction, gate = 1|
    EnvGen.kr(Env.asr(0, 1, 0), gate,doneAction:doneAction)
}).add;


~runTest = {  |return, waitBeforeRelease = 0.1|
    ~release = { |node|
        s.makeBundle(s.latency + waitBeforeRelease, {
            node.set(\gate, 0)
        });
        (s.latency + 0.3).wait
    };

    fork {
        ~functions.do { |func, i|
            return.(func.value, i)
        };
        ~waitBeforeRelease  = nil;
    };

};

~send = { |func|
    s.makeBundle(s.latency, func);
};


~functions = [

    // doneAction 0: do nothing when the UGen is finished
    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            h = Synth(\test_env_done_actions, [\doneAction, 1], g, \addToHead).register;
        });
        ~release.value(h);
        g.isPlaying and: { h.isPlaying }
    },

    // doneAction 1: pause the enclosing synth, but do not free it

    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            h = Synth(\test_env_done_actions, [\doneAction, 1], g, \addToHead).register;
        });
        ~release.value(h);
        g.isPlaying and: { h.isPlaying } and: { h.isRunning.not }
    },


    // doneAction 2: free the enclosing synth

    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            h = Synth(\test_env_done_actions, [\doneAction, 2], g, \addToHead).register;
        });
        ~release.value(h);
        g.isPlaying and: { h.isPlaying.not }
    },


    // doneAction 3: free both this synth and the preceding node

    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            h = Synth(\test_env_done_actions, [\doneAction, 3], g, \addAfter).register;
        });
        ~release.value(h);
        g.isPlaying.not and: { h.isPlaying.not }
    },


    // doneAction 4: free both this synth and the following node

    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            h = Synth(\test_env_done_actions, [\doneAction, 4], g, \addBefore).register;
        });
        ~release.value(h);
        g.isPlaying.not and: { h.isPlaying.not }
    },

    // doneAction 5: free this synth; if the preceding node is a group then do g_freeAll on it, else free it

    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            h = Synth(\test_env_done_actions, [\doneAction, 5], g, \addAfter).register;
            z = Synth(\test_env_done_actions, [\doneAction, 0], g, \addToHead).register;
        });
        ~release.value(h);
        g.isPlaying and: { h.isPlaying.not } and: { z.isPlaying.not }
    },

    // doneAction 6: free this synth; if the following node is a group then do g_freeAll on it, else free it

    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            h = Synth(\test_env_done_actions, [\doneAction, 6], g, \addBefore).register;
            z = Synth(\test_env_done_actions, [\doneAction, 0], g, \addToHead).register;
        });
        ~release.value(h);
        g.isPlaying and: { h.isPlaying.not } and: { z.isPlaying.not }
    },

    // doneAction 7: free this synth and all preceding nodes in this group

    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            h = Synth(\test_env_done_actions, [\doneAction, 7], g, \addToHead).register;
            x = Synth(\test_env_done_actions, [\doneAction, 0], g, \addToHead).register;
            y = Synth(\test_env_done_actions, [\doneAction, 0], g, \addToTail).register;
        });
        ~release.value(h);
        g.isPlaying and: { h.isPlaying.not } and: { y.isPlaying } and: { x.isPlaying.not }
    },

    // doneAction 8: free this synth and all following nodes in this group

    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            h = Synth(\test_env_done_actions, [\doneAction, 8], g, \addToHead).register;
            x = Synth(\test_env_done_actions, [\doneAction, 0], g, \addToHead).register;
            y = Synth(\test_env_done_actions, [\doneAction, 0], g, \addToTail).register;
        });
        ~release.value(h);
        g.isPlaying and: { h.isPlaying.not } and: { x.isPlaying } and: { y.isPlaying.not }
    },

    // doneAction 9: free this synth and pause the preceding node

    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            x = Synth(\test_env_done_actions, [\doneAction, 0], g, \addToTail).register;
            y = Synth(\test_env_done_actions, [\doneAction, 9], g, \addToTail).register;
        });
        ~release.value(y);
        y.isPlaying.not and: { x.isRunning.not }
    },


    // doneAction 10: free this synth and pause the following node

    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            x = Synth(\test_env_done_actions, [\doneAction, 10], g, \addToTail).register;
            y = Synth(\test_env_done_actions, [\doneAction, 0], g, \addToTail).register;
        });
        ~release.value(x);
        x.isPlaying.not and: { y.isRunning.not }
    },

    // doneAction 11: free this synth and if the preceding node is a group then do g_deepFree on it, else free it

    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            h = Synth(\test_env_done_actions, [\doneAction, 11], g, \addAfter).register;
            z = Synth(\test_env_done_actions, [\doneAction, 0], g, \addToHead).register;
        });
        ~release.value(h);
        g.isPlaying and: { h.isPlaying.not } and: { z.isPlaying.not }
    },

    // doneAction 12: free this synth and if the following node is a group then do g_deepFree on it, else free it

    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            h = Synth(\test_env_done_actions, [\doneAction, 12], g, \addBefore).register;
            z = Synth(\test_env_done_actions, [\doneAction, 0], g, \addToHead).register;
        });
        ~release.value(h);
        g.isPlaying and: { h.isPlaying.not } and: { z.isPlaying.not }
    },

    // doneAction 13: free this synth and all other nodes in this group (before and after)

    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            x = Synth(\test_env_done_actions, [\doneAction, 0], g, \addToHead).register;
            h = Synth(\test_env_done_actions, [\doneAction, 13], g, \addToHead).register;
            y = Synth(\test_env_done_actions, [\doneAction, 0], g, \addToHead).register;
        });
        ~release.value(h);
        g.isPlaying and: { h.isPlaying.not } and: { y.isPlaying.not } and: { x.isPlaying.not }
    },

    // doneAction 14: free the enclosing group and all nodes within it (including this synth)


    {
        var g, h, x, y, z;
        ~send.({
            g = Group(s).register;
            x = Synth(\test_env_done_actions, [\doneAction, 0], g, \addToHead).register;
            h = Synth(\test_env_done_actions, [\doneAction, 14], g, \addToHead).register;
            y = Synth(\test_env_done_actions, [\doneAction, 0], g, \addToHead).register;
        });
        ~release.value(h);
        g.isPlaying.not and: { h.isPlaying.not } and: { y.isPlaying.not } and: { x.isPlaying.not }
    },

];
)

////////////////////////////

// smaller than a block
~runTest.({ |works, i| i.post; " ".post; works.postln }, s.options.blockSize / s.sampleRate * 0.5);

// larger than a block, all ok
~runTest.({ |works, i| i.post; " ".post; works.postln }, s.options.blockSize / s.sampleRate * 2);

@vivid-synth
Copy link
Member

@vivid-synth vivid-synth commented Dec 2, 2016

Really appreciate these thorough unit tests. Would be great to add them to our test suite.

@telephon
Copy link
Member Author

@telephon telephon commented Dec 2, 2016

Yes! Would be nice to fix the issue first – I just don't know where the bug sits.

@patrickdupuis
Copy link
Contributor

@patrickdupuis patrickdupuis commented Jan 21, 2018

The issue is still present on 3.9.0. The "smaller than a block" test give varying results for some of the doneActions.

@patrickdupuis patrickdupuis added this to the 3.9.x milestone Jan 21, 2018
@patrickdupuis patrickdupuis modified the milestones: 3.9.x, 3.9.2 Jan 28, 2018
@nhthn nhthn removed the severe label Feb 17, 2018
@nhthn nhthn modified the milestones: 3.9.2, 3.9.x Feb 17, 2018
@sonoro1234
Copy link
Contributor

@sonoro1234 sonoro1234 commented Apr 11, 2018

On 3.9.2 windows scsynth I get the same problem
On supernova from master fails 6,8 and 12 with larger than block.

@sonoro1234
Copy link
Contributor

@sonoro1234 sonoro1234 commented Apr 12, 2018

let EnvGen_next_k print gate by uncomment this line
https://github.com/supercollider/supercollider/blob/develop/server/plugins/LFUGens.cpp#L3066

you will see that always that a test fails the gate is always 0.
That means that it is not EnvGen responsability and gate=1 never arrives to EnvGen because the server sends the second gate=0 value.
The places to look for are: Control and the mMapControls work (something too complicated for me)

In #3094 I am proposing a fix for other issues of EnvGen that lets early gate in the same status (but not worse)

@sonoro1234
Copy link
Contributor

@sonoro1234 sonoro1234 commented Apr 15, 2018

I did a test with EnvGen.ar with EnvGen_next_aa. The argument for a_gate was provided by an audio bus with a synth making the gate changes. Test was succesfull in scsynth and fails in supernova only for 6, 8, and 12.

My conclusion is that a gate shorter than control period in a control rate input is a nonsense: you will get 0 or 1 but not both values in the same control period, this will never happen "correctly" as the issue is trying to solve.

@jamshark70
Copy link
Contributor

@jamshark70 jamshark70 commented Apr 15, 2018

Sonoro is right - the reproducer as written is not internally consistent. Sub-block changes require audio rate signals.

Shall we keep this open for the supernova discrepancies, or close this as invalid?

@sonoro1234
Copy link
Contributor

@sonoro1234 sonoro1234 commented Apr 15, 2018

I would bet that the issue in supernova is with doneAction 6, 8 and 12 (not specifically with EnvGen) in general. (Could be tested with Line for example)

@telephon
Copy link
Member Author

@telephon telephon commented Apr 15, 2018

Sonoro is right - the reproducer as written is not internally consistent. Sub-block changes require audio rate signals.

The point is (if I remember correctly, this is pretty long ago) that if you have a kr envelope which is initiated with gate = 1 and then, within (including) the first block, the gate goes to zero, the envelope is stuck and never reaches its done action.

This may lead to hanging notes in some applications, and extra checks are not exactly trivial or beautiful.

@sonoro1234
Copy link
Contributor

@sonoro1234 sonoro1234 commented Apr 15, 2018

My conclusion was that if you have a kr envelope initiated with gate=1 and in the first block the gate goes to zero, half of the times you will get a 1 and the others you will get a zero.

If I am true this means that a limitation exists in scsynth that dont let you use in a kr argument variations shorter than the control block in any case. This is not specially bad except for gates!!

@jamshark70
Copy link
Contributor

@jamshark70 jamshark70 commented Apr 15, 2018

The point is (if I remember correctly, this is pretty long ago) that if you have a kr envelope which is initiated with gate = 1 and then, within (including) the first block, the gate goes to zero, the envelope is stuck and never reaches its done action.

I see, you're thinking of the language perspective and Victor is taking the server's.

From the server side, it is literally impossible to have a kr signal change in the middle of a control block.

The language can send a /s_new with gate being 1, and very quickly after, a /n_set with gate 0. The kr signal is, then, 0. But the UGen constructors might be processing a 1 during initialization, then unexpectedly receiving 0 during normal calculation. It wouldn't surprise me if EnvGen doesn't handle that case.

@telephon
Copy link
Member Author

@telephon telephon commented Apr 16, 2018

yes, exactly. From the server's point of view, it is a change from 1 to 0 in the initialization stage. Something goes wrong there.

It is a typical corner case, but it is relevant in practice: you may have no way to check what messages end up in a single bundle.

@telephon
Copy link
Member Author

@telephon telephon commented Jul 21, 2018

All tests work now.

@telephon telephon closed this Jul 21, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
6 participants