You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SuperCollider中Array.do()仅处理首个元素,无法批量释放合成器

Array.do()仅触发单个合成器释放的问题解决

问题现象

执行和弦播放代码后,2秒后仅第一个合成器的gate被设置为0,其余合成器无响应,控制台仅输出第一个合成器的释放日志。

相关代码

SynthDef定义

(
// 创建用于混合所有合成器的全局音频总线
~mainBus = Bus.audio(s, 2); // 立体声总线(2通道)

SynthDef(\squareSynthWithResonantFilter, {
    |outBus, freq = 440, filterStartFreq = 1000, filterEndFreq = 200, filterResonance = 0.1, amp = 0.2, sustain = 0.5, attack = 0.01, release = 0.5, gate = 1|
    var squareWave, ampEnv, filterEnv, filteredSignal, ampEnvGen, filterEnvGen;

    squareWave = Pulse.ar(freq, 0.5);

    ampEnv = Env.adsr(attack, 0.1, sustain, release);
    ampEnvGen = EnvGen.kr(ampEnv, gate, doneAction: 2);

    filterEnv = Env.adsr(attack, 0.1, sustain, release);
    filterEnvGen = EnvGen.kr(filterEnv, gate);

    filteredSignal = RLPF.ar(squareWave, filterEnvGen.linexp(0, 1, filterStartFreq, filterEndFreq), filterResonance);

    Out.ar(outBus, [filteredSignal, filteredSignal] * ampEnvGen * amp);
}).add;

原播放逻辑

// 同时按下并释放音符的播放例程
(
{
    var melody = [
        [440, 2000, 300, 0.9], // freq, filterStartFreq, filterEndFreq, filterResonance
        [523.25, 1500, 500, 0.6],
        [392, 2500, 400, 0.8],
        [349.23, 1000, 300, 0.5],
        [659.25, 2200, 200, 0.9],
        [447, 1800, 300, 0.4]
    ];

    var synths = [];
    // 此处array.do执行正常
    melody.do { |note|
        var synth = Synth(\squareSynthWithResonantFilter, [
            \outBus, ~mainBus, // 发送到全局总线
            \freq, note[0],
            \filterStartFreq, note[1],
            \filterEndFreq, note[2],
            \filterResonance, note[3],
            \amp, 0.3, // 固定振幅
            \sustain, 1.2,
            \attack, 0.02,
            \release, 1.0,
            \gate, 1 // 初始打开gate
        ]);
        synths.add(synth); // 存储合成器用于后续释放
        "Created synth with ID: %".format(synth.nodeID).postln; // 调试日志
    };

    2.wait; // 保持所有音符2秒

    // 尝试一起释放所有音符(仅第一个生效)
    synths.do { |synth|
        synth.set(\gate, 0);
        "Setting gate to 0 for synth: %".format(synth.nodeID).postln;
    };
}.fork;
)

全局限幅器

(
{
    var busSignal = In.ar(~mainBus, 2); // 从全局总线读取信号
    var limitedSignal = Limiter.ar(busSignal, 0.9); // 对混合信号应用限幅
    Out.ar(0, limitedSignal); // 将限幅后的信号输出到扬声器(立体声输出)
}.play;

控制台输出

Created synth with ID: 1013
Created synth with ID: 1014
Created synth with ID: 1015
Created synth with ID: 1016
Created synth with ID: 1017
Created synth with ID: 1018
Setting gate to 0 for synth: 1013

问题原因

核心问题是Routine闭包中的局部数组synths在wait操作后,引用可能被SuperCollider的垃圾回收机制回收,或者上下文丢失,导致数组仅保留第一个元素的引用。

解决方案

提供两种可靠的解决方式:

方式一:使用全局变量存储合成器引用

将局部变量synths替换为全局变量~synths,确保数组引用在Routine执行全程不会丢失:

(
{
    var melody = [
        [440, 2000, 300, 0.9],
        [523.25, 1500, 500, 0.6],
        [392, 2500, 400, 0.8],
        [349.23, 1000, 300, 0.5],
        [659.25, 2200, 200, 0.9],
        [447, 1800, 300, 0.4]
    ];

    ~synths = []; // 全局变量存储合成器
    melody.do { |note|
        var synth = Synth(\squareSynthWithResonantFilter, [
            \outBus, ~mainBus,
            \freq, note[0],
            \filterStartFreq, note[1],
            \filterEndFreq, note[2],
            \filterResonance, note[3],
            \amp, 0.3,
            \sustain, 1.2,
            \attack, 0.02,
            \release, 1.0,
            \gate, 1
        ]);
        ~synths.add(synth);
        "Created synth with ID: %".format(synth.nodeID).postln;
    };

    2.wait;

    ~synths.do { |synth|
        synth.set(\gate, 0);
        "Setting gate to 0 for synth: %".format(synth.nodeID).postln;
    };
    ~synths.clear; // 释放后清空全局数组
}.fork;
)

方式二:使用控制总线统一控制所有合成器的gate

创建全局控制总线管理所有合成器的gate状态,无需逐个遍历合成器,更高效且避免数组引用问题:

// 创建全局控制总线用于统一控制gate
~gateBus = Bus.control(s, 1);
~gateBus.set(1); // 初始设置gate为打开状态

// 修改SynthDef,从控制总线读取gate值
SynthDef(\squareSynthWithResonantFilter, {
    |outBus, freq = 440, filterStartFreq = 1000, filterEndFreq = 200, filterResonance = 0.1, amp = 0.2, sustain = 0.5, attack = 0.01, release = 0.5|
    var squareWave, ampEnv, filterEnv, filteredSignal, ampEnvGen, filterEnvGen, gate;

    gate = In.kr(~gateBus, 1); // 从控制总线读取gate状态

    squareWave = Pulse.ar(freq, 0.5);

    ampEnv = Env.adsr(attack, 0.1, sustain, release);
    ampEnvGen = EnvGen.kr(ampEnv, gate, doneAction: 2);

    filterEnv = Env.adsr(attack, 0.1, sustain, release);
    filterEnvGen = EnvGen.kr(filterEnv, gate);

    filteredSignal = RLPF.ar(squareWave, filterEnvGen.linexp(0, 1, filterStartFreq, filterEndFreq), filterResonance);

    Out.ar(outBus, [filteredSignal, filteredSignal] * ampEnvGen * amp);
}).add;

// 优化后的播放逻辑
(
{
    var melody = [
        [440, 2000, 300, 0.9],
        [523.25, 1500, 500, 0.6],
        [392, 2500, 400, 0.8],
        [349.23, 1000, 300, 0.5],
        [659.25, 2200, 200, 0.9],
        [447, 1800, 300, 0.4]
    ];

    melody.do { |note|
        var synth = Synth(\squareSynthWithResonantFilter, [
            \outBus, ~mainBus,
            \freq, note[0],
            \filterStartFreq, note[1],
            \filterEndFreq, note[2],
            \filterResonance, note[3],
            \amp, 0.3,
            \sustain, 1.2,
            \attack, 0.02,
            \release, 1.0
        ]);
        "Created synth with ID: %".format(synth.nodeID).postln;
    };

    2.wait;
    ~gateBus.set(0); // 一次性关闭所有合成器的gate
    "Released all synths via global gate bus".postln;
}.fork;
)

内容的提问来源于stack exchange,提问作者Ben Alan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 09:45:53