Munmap_chunk(): invalid pointer - memory issue?

I’ve just had a few issues with a patch I’m working on. I’ve opened an issue here, but after testing a bit more, I think it might actually be a problem with my actual code rather than a bug, so I thought I’d ask for feedback. Basically it’s a big reverb patch, and my very beginnery c++ chops lead me to wonder if I’m using too much memory, or using it poorly.

The error is:
munmap_chunk(): invalid pointer
Server ‘localhost’ exited with exit code 0.
server ‘localhost’ disconnected shared memory interface

This has turned up twice when I’ve updated the three synths (not every time though). Also, instantiating the reverb synth itself causes the server to quit instantly. Code is below, I’d love any feedback on this issue as it’s part of a major project and memory has been something I’ve been worried about anyway.

(
SynthDef(\hs, {
    |feedback = -3, scale = 1, amp = -20|
    var input, output;
    var early, late;
    var earlyTimes, lateTimes;
    var fb, tank, taps, outputDelays;
    var bands;
    var cd; 

// input
    input = In.ar(0, 2); // this is for testing, use SoundIn for performance
    input = input.sum;

    // early reflections - series
    early = input;
    earlyTimes = [142, 107, 379, 277] / 29761;
    4.do { |i|  early = AllpassC.ar(early, 0.5, earlyTimes[i], 1) }; // this may be worth experimenting with - AbsAllpass instead?

    // mix signal with feedback

    fb = LocalIn.ar(2);
    late = [early + fb[0], early + fb[1]] / 2; // maybe unnecessary to divide by two here because there will be gain control later on

    // split signal into bands w Linkwitz-Riley
    // <75, 75 - 180, 180 - 700, 700 - 2.5k, 2.5k>
    bands = Array.newClear(5);
    
    bands[0] = LPF.ar(LPF.ar(late, 75), 75);
    
    bands[1] = HPF.ar(HPF.ar(late, 75), 75);
    bands[1] = LPF.ar(LPF.ar(bands[1], 180), 180);
    
    bands[2] = HPF.ar(HPF.ar(late, 180), 180);
    bands[2] = LPF.ar(LPF.ar(bands[2], 700), 700);

    bands[3] = HPF.ar(HPF.ar(late, 700), 700);
    bands[3] = LPF.ar(LPF.ar(bands[3], 2500), 2500);
    
    bands[4] = HPF.ar(HPF.ar(late, 2500), 2500);

    // reverb tank function
   
    taps = List.new;
    tank = { 
        |in, allpassTimes = #[], decayTimes = #[], delayTimes = #[], fbIndex = 0, tapsIndex = 0|
        var sig;
        sig = AllpassC.ar(in, 0.1, allpassTimes[0] * scale, decayTimes[0]);
        taps.add(sig);
        sig = DelayC.ar(sig, 0.1, delayTimes[0]);
        sig = LPF.ar(sig, 9000);
        taps.add(sig); 
        sig = AllpassC.ar(sig, 0.1, allpassTimes[1] * scale, decayTimes[1]);
        sig = DelayC.ar(sig, 0.1, delayTimes[1] - ControlDur.ir);
        taps.add(sig); 
        fb[fbIndex] = fb[fbIndex] + sig;
    };
  
    
    // band 0, loops 0 + 1;

    tank.(bands[0][0], allpassTimes: [0.022579, 0.149625], decayTimes: [11.8, 11.8], delayTimes: [0.060481, 0.124995], fbIndex: 0);
    tank.(bands[0][1], allpassTimes: [0.030509, 0.141696], decayTimes: [11.8, 11.8], delayTimes: [0.089244, 0.10628], fbIndex: 1);


    // band 1 - these times are based off the others and scaled by various powers

    tank.(bands[1][0], allpassTimes: [0.023452, 0.152494], decayTimes: [12.8, 12.8], delayTimes: [0.062202, 0.127622], fbIndex: 0);
    tank.(bands[1][1], allpassTimes: [0.031593, 0.144491], decayTimes: [12.8, 12.8], delayTimes: [0.091427, 0.108689], fbIndex: 1);

    // band 2

    tank.(bands[2][0], allpassTimes: [0.033291, 0.148789], decayTimes: [10.7, 10.7], delayTimes: [0.094801, 0.112406], fbIndex: 0);
    tank.(bands[2][1], allpassTimes: [0.024824, 0.156902], decayTimes: [10.7, 10.7], delayTimes: [0.06487, 0.131665], fbIndex: 1);

    // band 3
    
    tank.(bands[3][0], allpassTimes: [0.03293, 0.14789], decayTimes: [9.4, 9.4], delayTimes: [0.094094, 0.111627], fbIndex: 0);
    tank.(bands[3][1], allpassTimes: [0.02453, 0.155981], decayTimes: [9.4, 9.4], delayTimes: [0.06431, 0.1308197], fbIndex: 1);

    // band 4
    
    tank.(bands[4][0], allpassTimes: [0.024073, 0.1545067], decayTimes: [6.3, 6.3], delayTimes: [0.06341843, 0.129467], fbIndex: 0);
    tank.(bands[4][1], allpassTimes: [0.032363, 0.146452], decayTimes: [6.3, 6.3], delayTimes: [0.092964, 0.110383], fbIndex: 1);

    // feedback signal

    fb = LeakDC.ar(fb);
    fb = LPF.ar(fb, 14000);
    fb = Limiter.ar(fb);
    fb = fb * feedback.dbamp;

    LocalOut.ar(fb);

// output processing 
    outputDelays = Array.newClear(6);
    6.do { |i| outputDelays[i] = taps[i] + taps[i+5] + taps[i+11] + taps[i+17] + taps[i+23]  }; // 6 taps from 5 signals

    output = [[ 
        DelayC.ar(outputDelays[0], 0.1, 0.008937),
        DelayC.ar(outputDelays[0], 1, 0.999294),
        DelayC.ar(outputDelays[1], 0.1, 0.064279).neg,
        DelayC.ar(outputDelays[2], 0.1, 0.067068),
        DelayC.ar(outputDelays[3], 0.1, 0.066866).neg, 
        DelayC.ar(outputDelays[4], 0.1, 0.006283).neg,
        DelayC.ar(outputDelays[5], 0.1, 0.035819).neg
    ].sum,
    [ 
        DelayC.ar(outputDelays[3], 0.1, 0.011861),
        DelayC.ar(outputDelays[3], 1, 0.121871),
        DelayC.ar(outputDelays[4], 0.1, 0.041262).neg,
        DelayC.ar(outputDelays[5], 0.1, 0.089816),
        DelayC.ar(outputDelays[0], 0.1, 0.070932).neg, 
        DelayC.ar(outputDelays[1], 0.1, 0.011256).neg,
        DelayC.ar(outputDelays[2], 0.1, 0.004066).neg
    ].sum
];
    
    output = output * amp.dbamp;

    Out.ar(0, output);

}).add;

SynthDef(\test, {
    var sig, env;
    env = Env.perc(0.01, 0.13).ar(Done.freeSelf);
    sig = Saw.ar(XLine.kr(1000, 100, 0.11));
    sig = sig ! 2 * -10.dbamp;
    Out.ar(0, sig);
}).add;

SynthDef(\kick, {
    var sig, env;
    env = Env.perc(0, 0.14).ar(Done.freeSelf);
    sig = SinOsc.ar(57 * (1 + Env.perc(0, 0.14, 22, -25).ar));
    sig = sig + (BPF.ar(PinkNoise.ar * -10.dbamp, 779));
    sig = (sig * 1.4).tanh * env;
    sig = sig ! 2 * -10.dbamp;
    Out.ar(0, sig);
}).add;
)

Your SynthDef is quite complex and uses many UGens, which could lead to high memory usage.

There might be an issue with pointer management. Check with a dev to run a debugger.

output processing section creates many DelayC UGens. Consider using a single multichannel DelayC

Consider using LocalBuf

Consider reducing to 3 or 4 bands to decrease complexity and memory usage

replace division with multiplication

Maybe some of the effect blocks are candidates for simple Ugens with faust?

Reverb = _ <: par(i, 5, band(i)) :> _
with {
    band(0) = fi.lowpass(2, 75);
    band(1) = fi.bandpass(2, 75, 180);
    band(2) = fi.bandpass(2, 180, 700);
    band(3) = fi.bandpass(2, 700, 2500);
    band(4) = fi.highpass(2, 2500);
};
1 Like

It doesn’t matter how complex your SynthDefs are, they should never crash scsynth. You can run out of RT memory, but then you should get an appropriate error message. However, some UGen might forget to do the necessary checks, so you end up with a memory error. Can you try with more realtime memory (ServerOptions.memSize)?

Either way, there is a bug in the C++ code.

1 Like

Maybe it’s a general corruption of the memory management structures.

Does the language never output this, even if there was mismanagement with memory?

It’s a server crash…

My startup.scd includes the line Server.all.do { |s| s.options.memSize = 1 << 17 }; - should I try even more memory?

EDIT: I tested with memSize = 2**20 and got the same error.

Which C++ code do you mean?

This is sort of what I was thinking, but my understanding of this is very very low…

Is there an efficiency difference? I thought multichannel DelayC Ugens would just expand to the same number of Ugens?

I thought about this, but would like to keep it as a last resort :slight_smile:

I haven’t checked this out yet, might be worth looking into!

Thank you both for the pointers in any case.

In one of the UGens, or maybe even in scsynth. BTW, do you get the same crash in Supernova?

Anyway, I’m pretty sure this is not an error on your side, so GitHub is probably the right place to investigate this further.

Yes, it also crashes with Supernova.

1 Like