Ben Alan
Ben Alan

Reputation: 1685

Array's do() function is only working on one element in the array

Here is my synthdef:

(
// Create a global audio bus for mixing all synths together
~mainBus = Bus.audio(s, 2); // Stereo bus (2 channels)

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;
)

I expected my code to play a chord and then release the notes after 2 seconds.

// Routine to play the composition with simultaneous press and release
(
{
    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 = [];
    // here array.do works as expected
    melody.do { |note|
        var synth = Synth(\squareSynthWithResonantFilter, [
            \outBus, ~mainBus, // Send to global bus
            \freq, note[0],
            \filterStartFreq, note[1],
            \filterEndFreq, note[2],
            \filterResonance, note[3],
            \amp, 0.3, // fixed amplitude
            \sustain, 1.2,
            \attack, 0.02,
            \release, 1.0,
            \gate, 1 // Start with gate open
        ]);
        synths.add(synth); // Store the synths for later release
        "Created synth with ID: %".format(synth.nodeID).postln; // Debugging
    };

    2.wait; // Hold all notes for 2 seconds

    // Release all notes together
    synths.do { |synth|
        synth.set(\gate, 0); // This only works on one of the notes
        "Setting gate to 0 for synth: %".format(synth.nodeID).postln;
    };
}.fork;
)

I wonder if this issue has something to do with the bus or limiter? Probably not.

// Apply global limiter to the mixed signal on the bus and send to main audio output
(
{
    var busSignal = In.ar(~mainBus, 2); // Read from the global bus
    var limitedSignal = Limiter.ar(busSignal, 0.9); // Apply limiter to the combined signal
    Out.ar(0, limitedSignal); // Output the limited signal to speakers (stereo out)
}.play;
)

The console output is this:

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

indicating that only the first synth has its gate set to 0.

Upvotes: 1

Views: 44

Answers (1)

ezra buchla
ezra buchla

Reputation: 364

the issue is not with the iteration, but with the population of the synths array.

the line var synths = [];

creates a new object of class Array, as can be verified by inspecting synths.class. Array is a fixed-capacity collection, and its .add method has some unusual behavior; from the helpfile:

Adds an item to an ArrayedCollection if there is space. This method may return a new ArrayedCollection. For this reason, you should always assign the result of add to a variable - never depend on add changing the receiver.

so your code can be made to work as expected by changing this line:

synths.add(synth); // this returns a new array with the new item added; 
                   // but the result is discarded

to:

synths = synths.add(synth);

Alternatively, you could initialize synths as a List rather than an Array, the former being extensible by design.

Upvotes: 2

Related Questions