supercollider

arduino programming via soundcard

with lots of study of the AudioBoot_V2_0 java code by chris at roboterclub-freiburg.de, i managed to write supercollider code for uploading sketches to an arduino via the soundcard. no ftdi chip needed!
it's a very cheap solution for programming barebone arduinos (well, atmega168 microcontrollers really). you only need a few resistors, a capacitor and a mega168. the only difficult part is to 'initialize' this microcontroller by burning the special bootloader on to it. this requires an avr programmer of some sort (STK500, USBtinyISP etc).

the preparation steps are as follows...
1. burn the bootloader on to a mega168
2. build the barebone circuit
3. prepare the arduino ide
4. install the RedArduino class

after that one can compile hex files in the arduino ide and upload them using supercollider and a standard audio cable.

1. burn the bootloader on to a mega168

the trick behind all this is the special 'sound enabled' bootloader that i found here... http://www.hobby-roboter.de/forum/viewtopic.php?f=4&t=127. i downloaded the AudioBoot_V2_0.zip file and used my stk500 avr programmer together with the great avr crosspack. the terminal command i used for burning the bootloader was the following...

avrdude -v -p m168 -b 115200 -P /dev/tty.PL2303-000013FA -c stk500v2 -U flash:w:/Users/asdf/arbeten/sc/scAudioino/AudioBoot_V2_0/Atmega_Source/chAudioBoot_ATMEGA168_IN_PD1_LED_PB5.hex -U lfuse:w:0xE2:m -U hfuse:w:0xDF:m -U efuse:w:0xFA:m

2. build the barebone circuit

then i built a minimal and barebone arduino circuit after the schematics found here... http://www.hobby-roboter.de/forum/viewtopic.php?f=4&t=128&p=531. again credit to chris.

this is the schematics i drew...

and here the resulting circuit...


i run it off 4,5v (3 batteries) but it could also be powered from the usb port.

3. prepare the arduino ide

to compile hex files for this barebone arduino, i needed to set up a custom board in the arduino ide. one way to do this is to create a new text file called boards.txt and put it inside a new folder in the arduino/hardware folder. on mac osx that could be something like ~/Documents/Arduino/hardware/BareBones/boards.txt. the boards.txt should contain the following...

minimal168.name=ATmega168 bare bone (internal 8 MHz clock)
minimal168.upload.speed=115200
minimal168.bootloader.low_fuses=0xE2
minimal168.bootloader.high_fuses=0xDD
minimal168.bootloader.extended_fuses=0×00
minimal168.upload.maximum_size=16384
minimal168.build.mcu=atmega168
minimal168.build.f_cpu=8000000L
minimal168.build.core=arduino:arduino
minimal168.build.variant=arduino:standard

then restart the arduino ide and under boards there should be a new option with mega168 and 8mhz internal clock. i make sure this board is selected every time before compiling hex files for sound uploading.
another thing that needs to be done is to enable 'verbose compile' in the arduino ide preferences. that will print out the file path of the hex file each time you compile a sketch.

4. install the RedArduino class

i wrote a couple of classes for supercollider to help with the encoding and signal generation of hex files. they're found in my redSys quark (under redTools) and are most easily installed from within supercollider itself with these commands...

Quarks.install("redSys");       //install. recompile after this

there are two helper classes and one main class. the RedIntelHex class parses hex files and RedDifferentialManchesterCodeNegative helps to encode the signal as differential manchester code. the main class RedArduino figures out the paging of data and generates a bit stream that is played back using demand rate ugens.

uploading

i upload sketches by compiling them in arduino ide (click verify - not upload) and copy&paste the file path of the resulting hex file into supercollider and the RedArduino's read method. i connect the left sound output channel to the barebone arduino, put my mac volume to ~80%. last i press the reset button on the circuit and quickly (within seconds) call the upload method in sc. the led should blink slowly directly after a reset, and fast when receiving data.

here's a video demonstrating how to do it. i'm just uploading the simple Blink example. near the end you will also hear how that sounds.

audioino from redFrik on Vimeo.

there's also some sc code i wrote here that will do the same thing but without the need of the redSys quark classes.

traer physics library for supercollider

a while ago i started porting the java/processing library TRAER.PHYSICS 3.0 by Jeffrey Traer Bernstein to supercollider. it's a simple and elegant particle system and a physics engine all in one. there are already ports to actionscript3, javascript and c++ (cinder), but i haven't seen anyone working with it in sc yet. so i had a go - both to learn more and to have an alternative to my own physics library quark redUniverse.

it is now finished and released as a quark. this is the initial version and there might still be bugs. i _did see sc crash once in a strange way after spawning lots of particles, so watch out for memory leaks.
to install it run the following code and recompile sc...

Quarks.install("TraerPhysics");
//and then recompile and open the help file TraerPhysicsOverview

i also wrote a few simple examples to go along with the helpfiles. here's a screenshot of one...

more sc twitter

more audio recordings of my twitter sctweets. see twitter.com/redFrik and this post.
normally you run these lines of code (140 characters) in supercollider and it will play you some kind of generative music or soundscape (also graphics in rare cases). here i've recorded a few for those who are too lazy to install sc.

 

//--tweet0026

{CombL.ar(In.ar(8).tanh/8,1,1,8)!2}.play;Pbind(\amp,8,\dur,1/4,\degree,Pseq(List.fib(32)%(List.fib(64)%12),inf),\out,8).play//#SuperCollider

this one is using the built in .fib (as in fibonacci) method to generate pitches (\degree) for the Pbind. the Pbind in it self is quite boring to listen to, so by playing it out on audio bus 8 and then making a small distortion+echo effect synth reading from bus 8, we get a much more interesting sound.

 

//--tweet0028

play{MoogFF.ar(LFTri.ar(CombN.ar(Duty.ar(1/8,0,Dseq(Dshuf(List.fib(16)%8*99,8),inf)),4,4,16))/4,LFTri.kr(1/16,0,2e3,3e3))!2}//#SuperCollider

again using the built in fibonacci method to generate patterns. here every 8th bar the pattern is scrambled (Dshuf), and in every 8th bar period the first 2 bars are transposed by a strange trick running the melody pattern (i.e. the frequencies) through a CombN.

 

//--tweet0030

play{a=LFPar;GVerb.ar(VarSaw.ar(a.ar(1,0,5,a.ar([0.05,0.04],0,50,160).round(50)),0,a.ar(0.2,0,0.5,a.ar(3,0,0.2,0.5)))/8,80)}//#SuperCollider

here is something that sounds a bit like a couple of trombones playing a riff in a reverberant room. the riff just goes on and on and is made from a pair of slowly changing LFPar oscillators, scaled, offset and rounded to the nearest 50Hz.

 

//--tweet0033

play{f=LFPar.ar(1/14).round*20+80;Splay.ar(LFPar.ar({|i|[i+1*f,i*f+(i+1/3)]}!4)>BrownNoise.ar(Pulse.ar({|i|i+1}!4,0.35))/3)}//#SuperCollider

sounds a bit like punk rock in 6/8 time signature. the crispness comes from the > BrownNoise combo and the rhythms from the Pulse. overall melody is the slowly running LFPar oscillator stored in variable f. note that this tweet only works in sc version 3.5 and above.

 

//--tweet0041

play{o=SinOsc.ar(1/RunningMax.ar(Sweep.ar(LocalIn.ar(6)),Impulse.ar([1,0.749,6,12,3,4])));LocalOut.ar(o);Splay.ar(o).tanh/2}//#SuperCollider

an ever rising tone cluster with some clicks. this is built using a localin/localout feedback chain. there are plateaus where one thinks the maximum frequency is reached, but those are only temporary and after a while the tone starts to rise again.

 

//--tweet0044

play{a=SinOsc;Limiter.ar(LeakDC.ar(a.ar(0.11,BRF.ar(a.ar(a.ar(0.12).exprange(1,1e4),2pi),1/a.ar(0.13).range(1,[99,100])))))}//#SuperCollider

here the cutoff frequency of a BRF (band reject filter) is modulated with a SinOsc. the cutoff varies between 1 and 99 Hz in the left channel, and 1 and 100 Hz in the right channel. the BRF goes wild and outputs totally crazy sounds when modulated in this matter - just like the BPF used to behave in old sc versions (3.3 and earlier).

 

//--tweet0045

play{a=SinOsc;a.ar(a.ar(a.ar(0.11)),a.ar(a.ar(95*a.ar(0.01,0,1,1),0,a.ar(5e-3,0,50),100),a.ar([98,97]),pi+a.ar(5e-4))).tanh}//#SuperCollider

a deep fat bass. it sounds as lovely in a big speaker system as it sounds poor in laptop speakers. the patch is mainly doing phase modulation on a SinOsc with tanh distortion.

 

//--tweet0046

play{a=LFTri;GVerb.ar(Mix(Limiter.ar(BRF.ar(a.ar(50,1e-4),a.ar(a.ar([1.01,1.0111])*a.ar(8e3)*1e3+4e3,55),a.ar(0.01)*3))))/9}//#SuperCollider

this tweet sounds much like a field recording. the noise comes from an exploding BRF (band reject filter) that is wrapped in a Limiter so that it keeps in range. last a GVerb is adding a metallic quality reverb to the overall sound.

 

//--tweet0047

play{CombN.ar(Limiter.ar(BRF.ar(LFSaw.ar(10,0,0.01),LFTri.ar([5,6]*0.1))),0.1,LFTri.kr(0.1,0,0.05,0.05).round(0.01))}//#SuperCollider#SC2012

this was coded, believe it or not, within a 5min time limit and under water pistol threat (part of sc2012 keynote talk in london). again it's a BRF misbehaving run through a comb delay with short modulated delaytime (from 0 to 0.1).

 

//--tweet0048

play{a=Impulse;b=SinOsc;c=b.ar(0,BRF.ar(a.ar([7,8]),a.ar(9).lag2(1e-3),1.5,2pi));Ringz.ar(c,b.ar(0.02,0,99,150),1/9)+c*0.02}//#SuperCollider

a quite poor tweet. the rhythms are not so interesting and it also have the problem of running out and stopping after a few seconds. anyway, the principle is that a BRF is generating strange sounds that phase modulate a SinOsc, that in turn goes through a ringing filter (Ringz). i only wanted to record it so that when someone fixes the BRF in some upcoming supercollider version, i can go back and listen to how it could sound.

 

//--tweet0053

Pbind(\freq,Pseq("SUPERCOLLIDER".ascii,inf)*Pstutter(64,Pseq([3,4,5],inf))*[1,2.045],\dur,0.03,\amp,Pseq([0,0.1],inf)).play// #SuperCollider

super annoying little thing. it is using the values of the ascii characters in the string "SUPERCOLLIDER" which is [83, 85, 80, 69, 82, 67, 79, 76, 76, 73, 68, 69, 82]. this is played in sequence and transposed and detuned. maybe a candidate for the official supercollider theme song?

 

//--tweet0054

play{CombN.ar(SyncSaw.ar(Saw.ar([3,4],32,64),Saw.ar([4,3],99,Duty.kr(1,0,flop(Dseq(2!6++4++3,99)*(4**(0..4))))))/9,1,1/6,2)}//#SuperCollider

very intense sounding tweet.

 

//--tweet0055

play{a=Pulse;CombN.ar(Slope.ar(a.ar(a.ar([1,2]/3,1/9,50,[50,150])),a.ar([3,4],1/3)+a.ar([2,3],1/4)/10+0.005).cos/5,1,1/6,2)}//#SuperCollider

an even more intense sounding tweet.

 

//--tweet0057

a=GVerb;fork{loop{z=play{#b,c,d,e,f,g,h,i=(1..50).scramble;a.ar(a.ar(a.ar(a.ar(Dust.ar(1),b,c),d,e),f,g),h,i)/20};6.wait;z.release(5)}}//#sc

this patch is heavy on the cpu. it consists of a synth with nested GVerbs all with random settings for roomsize and reverberation time. synths play for 6 seconds and then fades out over 5 seconds as another synth, with different reverb settings start. the result is overlapping sounds and quite dense texture.

 

//--tweet0059

a=LFTri;play{CombN.ar(SinOsc.ar(Saw.ar(3,128,128),Saw.ar([3,4],a.ar(a.kr(0.1,0,8,12),0,32,128)).sin)/4,1,1/6,a.kr(1/32)+1)}// #SuperCollider

a rhythmic tweet. gets a bit annoying after a while but there are some nice details in there.

 

//--tweet0061

a=Demand;b=SinOsc;play{b.ar(a.ar(t=Saw.ar([9,9.01]),0,Dseq(0!6++500,inf)),b.ar(a.ar(t,0,Dshuf((0..7)*99,inf)).lag(0.04)))/2}//#SuperCollider

phasing melody in left and right channels. every 7th note has a slightly different timbre (the 0!6++500 part) and every time one starts this tweet the melody changes (the Dshuf((0..7) part). the phasing is done with two Saw oscillators running at 9 and 9.01Hz. they are in turn used as triggers for the timbre and melody sequences (the two Demand ugens).

one reason why i love sc

140 characters...

play{Splay.ar(SinOsc.ar(9,SinOsc.ar(midicps((Sweep.ar(0,(33..3))%128&(Sweep.ar(0,(3..9))%(LFSaw.ar(3)*9+99)))+33),0,pi)))/3}//#SuperCollider

if we run this line in supercollider we hear this...


and using Rohan Drape's great rd_dot quark and swap play{} with draw{} in the line above, this mess gets revealed...

so with a single line of code - short enough to fit in a twitter tweet - we've built this amazingly complex sound synthesis patch. i can not imagine a system with greater code-to-noise ratio than supercollider.

and this one is pretty fun to .draw as well...

play{f={|o,i|if(i>0,{SinOsc.ar([i,i+1e-4]**2*f.(o,i-1),f.(o,i-1)*1e-4,f.(o,i-1))},o)};f.(60,6)/60}//#SuperCollider


a heavily recursive patch that looks almost fractal. the pdf file generated from this line of code is 300kb! the number 6 in the code means recursion depth and 60 is the base frequency.

to run the rd_dot quark in supercollider on a mac you'll need Graphviz (i use 2.28 on my osx 10.6.8). you also need to make sure the resulting .dot files opens automatically in graphviz and not photoshop, word or something (get info on a .dot file in Finder and change all filetypes to open with graphiviz. you'll find the .dot files from rd_dot in your hidden /tmp directory)

AttachmentSize
PDF icon 637231982.pdf85.87 KB
PDF icon 1909609728.pdf300.44 KB

soft modem

this summer i build 8 small circuits that can control a bunch of leds (6 channels pwm) from basically any idevice or android phone. the circuit connects to the audio jack of the phone and uses the right channel to send data commands (in the form of a modem signal).

we use rjdj (and pdlib, supercollider, etc) to generate the data signal on the phone in realtime. and it's relatively easy to connect for example the built-in accelerometer in the phone to control some leds, or to run amplitude/pitch tracking on the microphone and let that flash some leds.

the circuits will be used in the rhyme research project as well as in the upcoming e-textile workshop in oslo (oct 2011).

the design is based on SoftModem by arms22. attached below are my schematics, pd fsk abstraction and arduino firmware.

the modem signal is generated using frequency-shift keying and here's how to do that in supercollider...

(
c= "how are you?";
{var t= 1/1225; var m= Duty.ar(Dseq([t*100]++t.dup(11*c.size)), 0, Dseq([1]++c.ascii.collect{|cc| [0]++(cc.asBinaryString.ascii-48).reverse++[1, 1]}.flat), 2); SinOsc.ar(m*(7350-4900)+4900)!2}.play(fadeTime:0);
)

this will send the characters "how are you?" at a baudrate of 1225. this is of course not a valid command for the circuit above, just something to demonstrate how it sounds. below is an mp3...


AttachmentSize
Package icon softModem.zip61.64 KB

droneSines

a very simple supercollider drone instrument i wrote for a friend. the gui is automatically generated from the node proxy using the .edit method. most of the code is defining specs (min, max curve, stepsize and default values) for all the parameters. the synthesis part is very simple - just an oscillator going through a reverb and the oscillator is frequency, phase and amplitude modulated by other oscillators.

(
//redFrik 2011
s.waitForBoot{
        Spec.add(\freq1, #[20, 10000, \exp, 0, 100]);
        Spec.add(\freq2, #[20, 10000, \exp, 0, 200]);
        Spec.add(\freq3, #[20, 10000, \exp, 0, 300]);
        Spec.add(\fmod1, #[0.001, 100, \exp, 0, 0.11]);
        Spec.add(\fmod2, #[0.001, 100, \exp, 0, 0.22]);
        Spec.add(\fmod3, #[0.001, 100, \exp, 0, 0.33]);
        Spec.add(\fmoda1, #[0, 100, \lin, 0, 1]);
        Spec.add(\fmoda2, #[0, 100, \lin, 0, 1]);
        Spec.add(\fmoda3, #[0, 100, \lin, 0, 1]);
        Spec.add(\pmod1, #[0.001, 100, \exp, 0, 0.1]);
        Spec.add(\pmod2, #[0.001, 100, \exp, 0, 0.2]);
        Spec.add(\pmod3, #[0.001, 100, \exp, 0, 0.3]);
        Spec.add(\amod1, #[0.001, 100, \exp, 0, 0.01]);
        Spec.add(\amod2, #[0.001, 100, \exp, 0, 0.02]);
        Spec.add(\amod3, #[0.001, 100, \exp, 0, 0.03]);
        Spec.add(\amoda1, #[0, 10, \lin, 0, 0.05]);
        Spec.add(\amoda2, #[0, 10, \lin, 0, 0.05]);
        Spec.add(\amoda3, #[0, 10, \lin, 0, 0.05]);
        Spec.add(\smod, #[0.001, 100, \exp, 0, 0.13]);
        Spec.add(\smoda, #[0, 100, \lin, 0, 5]);
        Spec.add(\smodm, #[0, 100, \lin, 0, 6]);
        Spec.add(\smodaa, #[0, 100, \lin, 0, 8]);
        Spec.add(\smodmm, #[0, 100, \lin, 0, 50]);
        Spec.add(\cmod, #[0.001, 100, \exp, 0, 1.2]);
        Spec.add(\cmoda, #[0, 10, \lin, 0, 0.6]);
        Spec.add(\room, #[0, 300, \lin, 1, 20]);
        Spec.add(\reverb, #[0, 30, \lin, 0, 5]);
        Spec.add(\damp, #[0, 1, \lin, 0, 1]);
        Spec.add(\input, #[0, 1, \lin, 0, 0.5]);
        Spec.add(\spread, #[0, 100, \lin, 0, 25]);
        Spec.add(\dry, #[0, 1, \lin, 0, 0]);
        Spec.add(\early, #[0, 1, \lin, 0, 1]);
        Spec.add(\tail, #[0, 1, \lin, 0, 1]);
        Ndef(\droneSines).play;
        Ndef(\droneSines, {|freq1= 100, freq2= 200, freq3= 300, fmod1= 0.11, fmod2= 0.22, fmod3= 0.33, fmoda1= 1, fmoda2= 1, fmoda3= 1, pmod1= 0.1, pmod2= 0.2, pmod3= 0.3, amod1= 0.01, amod2= 0.02, amod3= 0.03, amoda1= 0.05, amoda2= 0.05, amoda3= 0.05, smod= 0.13, smoda= 5, smodm= 6, smodaa= 8, smodmm= 50, cmod= 1.2, cmoda= 0.6, room= 20, reverb= 5, damp= 1, input= 0.5, spread= 25, dry= 0, early= 1, tail= 1, amp= 0.7|
                Limiter.ar(LeakDC.ar(GVerb.ar(Splay.ar(SinOsc.ar([freq1, freq2, freq3]+SinOsc.ar([fmod1, fmod2, fmod3], 0, [fmoda1, fmoda2, fmoda3]), SinOsc.ar([pmod1, pmod2, pmod3], 0, 2pi), SinOsc.ar([amod1, amod2, amod3], 0, [amoda1, amoda2, amoda3])), SinOsc.ar(SinOsc.ar(SinOsc.ar(smod, 0, smoda, smodm), 0, smodaa, smodmm), 0, 1, 1), amp, SinOsc.ar(cmod, 0, cmoda)), room, reverb, damp, input, spread, dry, early, tail)));
        });
        Ndef(\droneSines).edit;
        s.meter;
};
)
//Ndef(\droneSines).clear;
Ndef(\droneSines).stop;

//--save a preset
Ndef(\droneSines).nodeMap.writeArchive("~/Desktop/pset1.txt".standardizePath)

//--recall a preset
Ndef(\droneSines).nodeMap= Object.readArchive("~/Desktop/pset1.txt".standardizePath)

//--scramble settings
(
Ndef(\droneSines).set(\amp, 0.05);
Ndef(\droneSines).controlKeys.do{|k|
        if(k!=\amp, {
                Ndef(\droneSines).set(k, k.asSpec.map(1.0.rand));
        });
};
)

Pages

Subscribe to RSS - supercollider