Preventing Microphone Feedback

I’d like to ask a question that might seem simple - but I’m not sure what to do. I’d like to use a microphone and a PA system, within my SuperCollider code, but I’m running into issues with feedback. I’ve attempted to use a notch filter with the Compander uGen, but it doesn’t seem to be working too well. The feedback seems to keep creeping in around the edges. I’m curious if this is a problem anyone here has come across and whether there are any interesting DSP solutions. I recall reading something about sensing the center frequency and adjusting a bank of notch filters accordingly - but I’m not entirely sure what the best way to do that would be.

Cheers!

Which microphone are you using and where is the mic positioned in relation to the speakers?

I’m using a dynamic mic (SM57) and I’m behind the speakers - I could definitely fine-tune those things more, if that’s considered the best way to deal with such things - but I am curious about the range of DSP techniques that might be relevant, also.

commercial feedback suppressors listen for the ringing to start and then notch out those frequencies. Would be a fun if non-trivial project to build…

Tangential, but here’s a piece that uses feedback suppression as creative tool!

Sometimes a small freqshift or a pitchshift can be a good tool to reduce the risk of feedback - not always a viable solution, but if it is more about ambience or impulse than pitch related sound it works really well.

commercial feedback suppressors listen for the ringing to start and then notch out those frequencies. Would be a fun if non-trivial project to build…

Would something like FluCoMa be the tool for this?

Very much influenced by Nic’s work:

Broken Symmetries

uses:

PV_Control

I need to update the help file with that Ugen though…if someone is interested in using this, I can do so.

But it won’t help the OP. In that case, you just need to use an EQ or turn down the mic gain. If you are using comb filters or convolution or similar processes that have a tendency to reinforce frequencies, then there is almost nothing you can do to prevent feedback. The solution is to not use those processes or to delay them enough so there is no issue.

Sam

3 Likes

The “other” Nick Collins uses a limiter to control feedback.

I have to remember this so I can play with feedback more

I had assumed, without watching, that the top piece was by the “other” Nick. PeaSoup is the inspiration for my work. We performed it at Birmingham in 2006. Very world-changing experience (thanks Scott!!).

The crazy thing for me, seeing Nic’s Roomtone Variations, is that I made the EXACT same piece in 2011:

Selbsportrait mit Peter Ablinger

I think I even use the same notation library. Great minds think alike!?

Sam

4 Likes

Hi @Sam_Pluta I’m really interested in using PV_Control. If you have a spare moment for a basic usage example that would be really appreciated.

Also, this video also shows some creative use of feedback suppression using Max from about 19 mins

It would be great to port everything in this video to SC. It all seems incredibly useful. I wonder if @tremblap has ported any of it or if it only exists in Max still? I’m interested to try when I have some free time.

Hello

These tools help tame system nodes indeed. Now, porting them to SC is not something on my radar…

the HISSTools Impulse Response Toolbox was made for max and had a few real-time elements, but most are NRT and could be ported as Buffer offline processes like some other stuff I’ve done. THe RT convo reverb is also quite efficient and has a cool interface compare to PartConv

If there is an interest, the code is out there and there is a more efficient and modern, header only version of the needed algo here. We could start another thread as it gets far from feedback removal :slight_smile:

1 Like

This is excellent info, thanks. I will indeed start a new thread if I have any questions!

1 Like

It took me a minute to cook up this example, which is in the end very similar to the example in the PV_Control help file. Run the code then very slowly turn up the volume until the signal starts feeding back. Move the microphone around or adjust the EQ on in output signal and the whole thing should change. I am getting good results with this, but it took me a bit to figure the instrument out. Feedback is funny because every room is different. Every mic is different. Etc. I feel like it is different every time.

This won’t totally blow up, but do be careful with the volume when you first try it out. Don’t fry your ears!

Sam

(
var feedback_innards = {|in, buf, buf2, num_analyses, thresh, mulFactor, attackReleaseFrames, sustainZeroFrames, waitGoFrames, tripCount, tripBlockFrames, topBin, ampMin, qRatio|
    var fft, windowStarts, vols, bandwidth, center;
    
    in = Compander.ar(in, in, 0.5, 1, 0.5, 0.01, 0.01);

    //calculate the fft
    fft = FFT(buf, in);

    //
    fft = PV_Control(fft, buf2, thresh, mulFactor, attackReleaseFrames+Rand(-10,10), sustainZeroFrames+Rand(-10,10), waitGoFrames+Rand(-50,50), tripCount+Rand(-2,2), tripBlockFrames+Rand(-50,50), topBin);

    //read the volumes from the pv_control fft buffer
    vols = num_analyses.collect{|i|
        BufRd.kr(1,buf2,i*2+2,0,0);
    };

    bandwidth = SampleRate.ir/2048;

    //make a bunch of MidEQ filters - one per fft band
    num_analyses.do{arg i;
        var center = (i+1)*(bandwidth);
        in = MidEQ.ar(in, center, qRatio*(bandwidth/center), vols[i].lincurve(0,1,ampMin,1,-4).ampdb);
    };

    in
};

SynthDef("feedback_control_example", {|inBus, speaker_outBus=0, sax_outBus = 4, volume=0.8, thresh=5, mulFactor=0.11, attackReleaseFrames=200, sustainZeroFrames=399, waitGoFrames=138, tripCount=20, tripBlockFrames=100, topBin=400, ampMin=0, qRatio=2|
    var fx1, fx2;
    var numBins = 2048;
    var num_analyses = 300;

    //make the 2 needed fft buffers
    var buf = LocalBuf(numBins);
    var buf2 = LocalBuf(numBins);

    var sig  = SoundIn.ar(inBus);
    var env = Env.asr(0.1, 1, 0.1).kr(2, \gate.kr(1));

    //lowPass
    3.do{sig = LPF.ar(sig, 1000)};
    sig = VSTPlugin.ar(sig.dup, 2, 0, id:\rr3_1);
    
    sig = feedback_innards.(Mix(sig), buf, buf2, num_analyses, thresh, mulFactor, attackReleaseFrames, sustainZeroFrames, waitGoFrames, tripCount, tripBlockFrames, topBin, ampMin, qRatio);  
    
    //lots of verb is good
    sig = FreeVerb.ar(sig, 1, 0.95);

    //distiortion is necessary to amplify the signal and limit the output volume
    sig = (sig*2).tanh*0.75;



    Out.ar(speaker_outBus, sig.dup.flatten*volume);

}).load(s);
)

(
    var input_channel = 0; //set your input channel here (probably 0)

    (
        s.waitForBoot({
            a = Synth("feedback_control_example", [\inBus, input_channel]);
            b = [
                [\thresh, ControlSpec(0,10), 7, StaticText()],
                [\mulFactor, ControlSpec(0,1), 0.11, StaticText()],
                [\attackReleaseFrames, ControlSpec(10,500, 'lin', 1), 300, StaticText()],
                [\sustainZeroFrames, ControlSpec(10,500, 'lin', 1), 399, StaticText()],
                [\waitGoFrames, ControlSpec(0,300, 'lin', 1), 138, StaticText()],
                [\tripCount, ControlSpec(0,20, 'lin', 1), 3, StaticText()],
                [\tripBlockFrames, ControlSpec(0,500, 'lin', 1), 400, StaticText()],
                [\topBin, ControlSpec(10,500, 'lin', 1), 400, StaticText()],
                [\volume, ControlSpec(0,1), 0, StaticText()]
            ];

            a.set(*b.collect{|item| [item[0], item[2]]}.flatten);
        
            c = b.collect({|item| Slider().orientation_('horizontal')
                .action_{|sl|
                    a.set(item[0], item[1].map(sl.value));
                    item[3].string_(item[1].map(sl.value).round(0.01).asString)
            }});
        
            c.do{|item, i| {item.valueAction_(b[i][1].unmap(b[i][2].postln))}.defer};
        
            w = Window();

            w.onClose = {a.set(\gate,0)};
        
            w.layout_(
                HLayout(
                    VLayout(*b.collect{|item| StaticText().string_(item[0])}),
                    VLayout(*c),
                    VLayout(*b.collect{|item| item[3]})
            ));
            w.front;
        });
        )
)

Sam

1 Like

This is great - lots to play with. Many thanks for your help!