0

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

我需要它:分段合成。我想要例如第一个和第二个巴生之间 50 毫秒的 xfade,然后是第二个和第三个巴生之间的 50 毫秒 xfade,然后是第三个和第四个巴生之间的 50 毫秒 xfade,依此类推,希望这个串联作为一个整体被一个包络调制.

不幸的是,EnvGen 似乎在播放连续巴生对的循环的每次迭代中都从头开始重新启动。我想要一个poiiiiinnnnnnnnnng,但无论我尝试什么,我得到的都是popopopopopopopopopo。

2019年编辑:

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

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

在这个程序中发生的情况是,EnvGen 似乎在每次循环迭代时重新启动:包络从 t=0 重新开始。我希望得到一个 1 秒长的呈指数衰减的声音,就像拨弦一样。由于信封在每次循环迭代开始时重新启动,我得到的是一系列 100 毫秒的“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;

(这个问题,我最初挣扎了几个月但没有成功,实际上导致我把学习 SuperCollider 的努力搁置了两年,现在我要从上次中断的地方继续。)

4

1 回答 1

1

你在这里工作的方式有点不寻常。

使用 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
});

我已经改变了更长的包络和音高的变化,所以你可以听到所有的振荡器开始。

我所做的是定义了两个 SynthDef 和一个总线。

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

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

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

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

我希望漫长的等待答案并没有让你永久关闭。您可能会发现查看一些教程并以这种方式开始很有帮助。

于 2020-12-07T15:52:58.000 回答