Other things on this site...

MCLD
music
Evolutionary sound
Listen to Flat Four Internet Radio
Learn about
The Molecules of HIV
MCLD
software
Make Oddmusic!
Make oddmusic!

Cross-correlation signal detection in SuperCollider

If you had to communicate a digital message by playing sound across a noisy room, how would you do it?

That's basically one of the problems signal processing engineers have worked on for decades, and there are many good ways to do it (the same principles allow mobile phones to communicate in a "noisy" radio spectrum too). One thing you can do is spread-spectrum signalling, using a particular signature spread across various different frequency bands. For example, an upwards "chirp" can be more robust than a simple bleep at a single frequency.

I just found this code example I put on the sc-users mailing list. It's quite nice - you use an upwards-chirp and a downwards-chirp, and detect which one has happened, even though we deliberately add loads of noise. (The code uses a technical trick, where we can do cross-correlation signal detection by convolution with the time-reversed signal.)

Server.default = s = Server.internal;
s.boot;
~dursamps = 16384;
~dursecs  = ~dursamps / s.sampleRate;

(
SynthDef(\chirpup, { |dur=0.1|
       var sig = SinOsc.ar(Line.ar(1000, 10000, dur, doneAction: 2));
       Out.ar(0, Pan2.ar(sig * 0.1));
}).add;
SynthDef(\chirpdn, { |dur=0.1|
       var sig = SinOsc.ar(Line.ar(10000, 1000, dur, doneAction: 2));
       Out.ar(0, Pan2.ar(sig * 0.1));
}).add;
SynthDef(\chirprecord, { |dur=0.1, in=0, buf=0|
       var sig = In.ar(in);
       RecordBuf.ar(sig, buf, loop: 0, doneAction: 2)
}).add;
)

~group_chirp    = Group.new(s);
~group_analysis = Group.after(~group_chirp);

////////////////////////////////////
// analysis

// OK now let's create a buffer, then put a frame of data in
~upbuf = Buffer.alloc(s, ~dursamps);
~dnbuf = Buffer.alloc(s, ~dursamps);
(
s.bind{
       Synth(\chirpup , [\dur, ~dursecs], ~group_chirp);
       Synth(\chirprecord, [\dur, ~dursecs, \buf, ~upbuf], ~group_analysis);
}
)
// ...wait for one to finish before doing the next:
(
s.bind{
       Synth(\chirpdn , [\dur, ~dursecs], ~group_chirp);
       Synth(\chirprecord, [\dur, ~dursecs, \buf, ~dnbuf], ~group_analysis);
}
)

// reverse them
~upbuf.loadToFloatArray(action: {|data| ~upRbuf = Buffer.loadCollection(s, data.reverse)});
~dnbuf.loadToFloatArray(action: {|data| ~dnRbuf = Buffer.loadCollection(s, data.reverse)});

//////////////////////////////////////
// detection

(
SynthDef(\chirpdetector, { |in=0, upbuf=0, dnbuf=0, framesize=100, out=0|
       var sig = In.ar(in);
       var conv = [upbuf, dnbuf].collect{|buf| Convolution2.ar(sig, buf, 0,
framesize) };
       var smooth = 0.1 * Amplitude.ar(conv, 0, 0.2);
       smooth[0].poll(smooth[0] > 1, "UP");
       smooth[1].poll(smooth[1] > 1, "DN");
       Out.ar(out, smooth);
}).add;
)

~detectbus = Bus.audio(s, 2);
x = Synth(\chirpdetector, [\framesize, ~dursamps, \upbuf, ~upRbuf, \dnbuf, ~dnRbuf, \out, ~detectbus], ~group_analysis);
~detectbus.scope

// Let's add some noise too, to make it a difficult task:
~noise = {WhiteNoise.ar(0.3)}.play(~group_chirp);

// Now we trigger different types of chirp and watch how the output leaps...
// RUN THIS LINE OVER AND OVER, waiting imbetween:
Synth([\chirpdn, \chirpup].choose, [\dur, ~dursecs], ~group_chirp);

The original discussion is here on the sc-users mailing list.

| supercollider | Permalink

social