为什么EnvGen会在每次循环迭代时重新启动以及如何防止这种行为?

时间:2017-09-08 21:23:20

标签: supercollider

如何在循环中使用EnvGen,使其在循环的每次迭代都不会重启?

我需要它:分段合成。我想要,例如在第一和第二巴生之间50ms的xfade,然后在第二和第三巴生之间的50ms xfade,然后在第三和第四巴生之间的50ms xfade,依此类推, 我希望这个连接作为一个整体由信封调制。

不幸的是,EnvGen似乎从播放连续Klang对的循环的每次迭代开始重新开始。我想要一个poiiiiinnnnnnnnnnng,但无论我尝试什么,我得到的是popopopopopopopopo。

2019年编辑:

好的,既然没有人会回答“如何实现目标”的问题,我现在将这个问题降级为“为什么这种特殊方法不起作用”,也改变了标题。

在我粘贴一些代码之前,先解释一下:这是一个非常简单的例子。虽然我最初的愿望是用一个信封来调制一个复杂的,分段生成的声音,但这个简化的例子只是从一个SinOsc的输出中“剪切”100ms段,只是为了人为地创造“分段生成”的情况。

此程序中发生的事情是EnvGen似乎在每次循环迭代时重新启动:包络从t = 0重新启动。我希望得到一个1s长的指数衰落的声音,比如拔弦。由于信封在每次循环迭代开始时重新启动,我得到的是一系列100ms“ping”。

如何防止这种情况发生?

以下是代码:

//Exponential decay over 1 second
var envelope = {EnvGen.kr(Env.new([1,0.001],[1],curve: 'exp'), timeScale: 1, doneAction: 2)};

var myTask = Task({

    //A simple tone
    var oscillator = {SinOsc.ar(880,0,1);};

    var scissor;

    //Prepare a scissor that will cut 100ms of the oscillator signal
    scissor = {EnvGen.kr(Env.new([1,0],[1],'hold'),timeScale: 0.1)};
    10.do({

        var scissored,modulated;

        //Cut the signal with the scisor
        scissored = oscillator*scissor;

        //Try modulating with the envelope. The goal is to get a single 1s exponentially decaying ping.
        modulated = {scissored*envelope};

        //NASTY SURPRISE: envelope seems to restart here every iteration!!!
        //How do I prevent this and allow the envelope to live its whole
        //one-second life while the loop and the Task dance around it in 100ms steps?
        modulated.play;

        0.1.wait;
    });
});

myTask.play;

(这个问题,我最初在没有成功的情况下为MONTHS而努力,实际上让我搁置了我学习SuperCollider两年的努力,现在我正在接受我离开的地方。)

1 个答案:

答案 0 :(得分:0)

您在这里工作的方式与众不同。

使用SuperCollider,您正在寻找的范式转变是将SynthDefs创建为离散实体:

s.waitForBoot ({
    b = Bus.new('control');
    
    SynthDef(\scissors, {arg bus;
        var env;
        env = EnvGen.kr(Env.linen);
        //EnvGen.kr(Env.new([1,0.001],[1],curve: 'exp'), timeScale: 1, doneAction: 2);
        
        Out.kr(bus, env);
    }).add;
    
    
    SynthDef(\oscillator, {arg bus, out=0, freq=440, amp = 0.1;
        
        var oscillator, scissored;
        oscillator = SinOsc.ar(freq,0,1);
        scissored = oscillator * In.kr(bus) * amp;
        
        Out.ar(out, scissored);
    }).add;
    
    s.sync;
    
    Task({
        Synth(\scissors, [\bus, b]);
        s.sync;
        10.do({|i|
            
            Synth(\oscillator, [\bus, b, \freq, 100 * (i+1)]);
            0.1.wait;
        });
    }).play
});

我已经更改了更长的包络和音调,因此您可以听到所有振子的响动。

我要做的是定义了两个SynthDefs和一条总线。

第一个SynthDef有一个信封,出于听觉上的考虑,我将其加长了。它将信封的值写到总线上。这样,每个其他想要使用共享信封的SynthDef都可以通过读取总线来获取它。

第二个SynthDef具有一个SinOsc。我们将其输出乘以总线输入。这使用共享包络来更改幅度。

这个“有效”,但是如果您第二次运行它,您将得到另一个讨厌的惊喜!振荡器SynthDefs尚未结束,您将再次听到它们。为了解决这个问题,您需要为他们提供自己的信封或带有doneAction的其他物品。否则,它们将永远存在。在每个单独的振荡器合成器上放置包络线也是塑造每个振荡器合成器的好方法。

在此示例中您可能会注意到的另一件事是s.sync;行。 SuperCollider的主要功能是音频服务器和语言是独立的进程。那条线确保了服务器已经赶上了,所以我们在它们准备好之前不要尝试使用服务器端资源。客户端/服务器的这种分离也是为什么最好在使用synthdef之前定义它们的原因。

我希望长时间等待答案不会使您永久关闭。您可能会发现查看一些教程并以这种方式入门会很有帮助。