‹ serialAudioAndroSensor ›

FSK

2014-10-18 21:36:28 supercollider

clean-up: #54

For a few projects in the past, I had to communicate data bidirectionally via sound. It involved, for example, hooking up a microcontroller to an iPod Touch (running SuperCollider) so that the device could read sensors and/or control LEDs. One successful method was to do Frequency-shift keying. See /f0blog/soft-modem/ for another blog entry on that.

I've published FSK Pure Data code for that before, and also some SuperCollider code for generating/encoding FSK, but never the decoding part I think.

So below is some old code I've cleaned up a bit. It just demonstrates sending audio via internal SuperCollider busses at a very low baud rate. For good baud rate and min/max frequency settings see code.google.com/p/arms22/source/browse/trunk/SoftModem/SoftModem.h.

(
s.latency= 0.05;
s.waitForBoot{
    SynthDef(\redFSKdecode, {|in= 20, thresh= 0.1, baudrate= 126, lo= 882, hi= 1764|
        var sig= InFeedback.ar(in, 1);
        var sigActive= Amplitude.ar(sig, 0.01, 0.01)>thresh;
        var fre= 1/Timer.ar(sig*sigActive);
        var trg= Schmidt.ar(fre, lo+(baudrate*0.5), hi-(baudrate*0.5));  //0= lo, 1= hi
        var trgLo= trg<0.5;
        var trgHi= 1-trgLo;
        var trgCarr= Sweep.ar(trgHi, 1)>(1/baudrate*16);  //when found more than 16bits high in a row
        var trgData= SetResetFF.ar(trgLo*trgCarr, trgCarr);
        var imp= Phasor.ar(trgData, baudrate/SampleRate.ir);
        var writeTrg= (imp-Delay1.ar(imp))<0+Impulse.ar(0);
        var writePos= PulseCount.ar(writeTrg, trgData);
        var writeVal= Median.ar(31, trg);
        var ok, done= 1-trgData;
        var buf= LocalBuf(13).clear;
        Demand.ar(writeTrg, 0, Dbufwr(writeVal, buf, writePos, 0));
        ok= done*(1-Demand.ar(done, 0, Dbufrd(buf, 1, 0)))*Demand.ar(done, 0, Dbufrd(buf, 11, 0));
        SendReply.ar(ok, '/data', Demand.ar(ok, 0, Dbufrd(buf, (2..9), 0)));
        DC.ar(0);
    }).add;
    SynthDef(\redFSKencode, {|out= 0, amp= 0.6, minFreq= 4900, maxFreq= 7350, invBaudrate= 0.008|
        var data= Control.names([\data]).ir(Array.fill(8, 0));
        var env= EnvGen.ar(Env(#[1, 1, 0], [invBaudrate*(16+11), 0]), doneAction:2);
        var parity= data.sum+1;
        var freq= Duty.ar(Dseq([invBaudrate*16]++invBaudrate.dup(11)), 0, Dseq(#[1, 0]++data++[parity%2, 1]));
        var src= SinOsc.ar(freq*(maxFreq-minFreq)+minFreq, 0, amp);
        OffsetOut.ar(out, src*env);
    }).add;
};
)

//test sending a simple counter
(
var baudrate= 126;
var lo= 882;
var hi= 1764;
OSCFunc({|msg|
    var byte;
    //("received:"+msg[3..]).postln;
    byte= msg[3..].sum{|x, i| 2**i*x};
    ("received:"+byte).postln;
}, '/data');
Routine.run({
    Synth(\redFSKdecode, [\in, 20]);  //bus 20
    inf.do{|i|
        var byte= (i%256).asInteger;
        var data= byte.asBinaryDigits.reverse;
        s.bind{
            ("sending :"+byte).postln;
            Synth(\redFSKencode, [\minFreq, lo, \maxFreq, hi, \data, data, \out, 20, \invBaudrate, 1/baudrate]);  //bus 20
            Synth(\redFSKencode, [\minFreq, lo, \maxFreq, hi, \data, data, \out, 0, \invBaudrate, 1/baudrate]);  //monitor
        };
        (1/baudrate*(11+16)).wait;
    };
})
)

//test a string of text
(
var baudrate= 126;
var lo= 882;
var hi= 1764;
OSCFunc({|msg|
    var byte;
    //("received:"+msg[3..]).postln;
    byte= msg[3..].sum{|x, i| 2**i*x};
    ("received:"+byte.asInteger.asAscii).postln;
}, '/data');
Routine.run({
    Synth(\redFSKdecode, [\in, 20]);  //bus 20
    "hello supercollider!".do{|x|
        var byte= x.ascii.clip(0, 255);
        var data= byte.asBinaryDigits.reverse;
        s.bind{
            //("sending :"+x+byte+data).postln;
            Synth(\redFSKencode, [\minFreq, lo, \maxFreq, hi, \data, data, \out, 20, \invBaudrate, 1/baudrate]);  //bus 20
            Synth(\redFSKencode, [\minFreq, lo, \maxFreq, hi, \data, data, \out, 0, \invBaudrate, 1/baudrate]);  //monitor
        };
        (1/baudrate*(11+16)).wait;
    };
})
)

This FSK technique was used in many projects to control LEDs. e.g. most of the 3rd gen projects on RHYME are running SuperCollider on an iPod Touch with the 6 channels led brightness data being sent via FSK on one of the audio output channels.

‹ serialAudioAndroSensor ›