‹ serialAudio AndroSensor ›

FSK

2014-10-18 21:36 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.


‹ serialAudio AndroSensor ›