supercollider

growing sounds

clean-up #13:

this is a demonstration of a simple sequencer using the RedGA library - a library for genetic algorithms. see this story for more info and sourcecode.

the whole program consists of six voices. for each voice there are individual genomes for the synthdef as well as for the amplitude pattern (the step-sequencer) and for the envelope shape. the genomes are all randomised at startup or when the 'restart' button is clicked. one can monitor the complete result of one voice (synthdef+pattern+envelope) with the 'play' button and edit the detailed multislider views manually if desired. as the play button is clicked, the synthdef genome is translated to a phenome (a real working synthdef) and sent to the server as well as posted to the post window.
with the 'mark' buttons one select which voices that will be parents in the next generation. one can mark from zero up to all six voices, but a more sensible number is two or three. there is the choice of keeping the parent voices alive to the next generation, or to overwrite them with new children.
in any case, this program is not meant to be taken completely serious. it is more of a fun toy using genetic algorithm to create some chaotic and glitchy little sounds.

below is the printout of the genomes (arrays) and phenomes (synthdefs) that were produced in the video demonstration.

GENOME:
[ 0.97673571109772, 0.25465285778046, 0.42777442932129, 0.67972803115845, 0.33054959774017, 0.6037335395813, 0.56670892238617, 0.90849816799164, 0.082634687423706, 0.60306036472321, 0.39336848258972, 0.40707266330719, 0.0089198350906372, 0.58077096939087, 0.79928719997406, 0.46543490886688, 0.58306109905243, 0.2658509016037, 0.2371027469635, 0.65999948978424 ]

PHENOME:
(SynthDef('RedGAPhenomeDef_101128_015549', {|out= 0, amp= 1, gate= 1| Out.ar(out, Limiter.ar(LeakDC.ar(VarSaw.ar(SinOsc.kr(Dust.kr(LFSaw.kr(WhiteNoise.kr(WhiteNoise.kr(0.659999).range(0.2658509016037, 0.2371027469635)).exprange(16.005815279484, 9.362154686451)).exprange(8153.3118128777, 198.21830511093)).exprange(1.7444302797318, 12.100901257992)).exprange(12082.596120834, 11342.844269276)).range(-0.14445114135742, 0.35945606231689)*EnvGen.kr(#[ 0, 2, 1, -99, 1, 0.01, 5, -4, 0, 0.02, 5, -4 ], gate, amp, 0, 1, 2))))}).play)

GENOME:
[ 0.20719468593597, 0.044587969779968, 0.080502510070801, 0.57578802108765, 0.082075834274292, 0.75569093227386, 0.49076271057129, 0.017484903335571, 0.86507594585419, 0.24153673648834, 0.65859174728394, 0.0708167552948, 0.86804354190826, 0.01993727684021, 0.6159542798996, 0.10451817512512, 0.55506300926208, 0.35075616836548, 0.57417559623718, 0.94203984737396 ]

PHENOME:
(SynthDef('RedGAPhenomeDef_101128_015552', {|out= 0, amp= 1, gate= 1| Out.ar(out, Limiter.ar(LeakDC.ar(SinOscFB.ar(111.474092, VarSaw.ar(LFNoise2.ar(32.619722, 0.868044, -0.960125).exprange(4845.9039950371, 13178.663110733)).range(0.49076271057129, 0.017484903335571)).range(-0.8389949798584, 0.15157604217529).perform('sqrdif', LFSaw.ar(1055.739533, 1.884080).range(0.11012601852417, -0.29848766326904))*EnvGen.kr(#[ 0, 2, 1, -99, 1, 0.01, 5, -4, 0, 0.02, 5, -4 ], gate, amp, 0, 1, 2))))}).play)

GENOME:
[ 0.093999624252319, 0.045899748802185, 0.94297802448273, 0.28493881225586, 0.53934347629547, 0.98470652103424, 0.042535185813904, 0.16906559467316, 0.80304133892059, 0.043324589729309, 0.060743093490601, 0.010264873504639, 0.48678374290466, 0.13158559799194, 0.052134871482849, 0.19801700115204, 0.09520161151886, 0.15453398227692, 0.5274965763092, 0.16556298732758 ]

PHENOME:
(SynthDef('RedGAPhenomeDef_101128_015556', {|out= 0, amp= 1, gate= 1| Out.ar(out, Limiter.ar(LeakDC.ar(SinOscFB.ar(LFNoise1.kr(0.244920, 0.803041, -0.913351).exprange(19694.436290264, 869.8530125618)).range(0.88595604896545, -0.43012237548828).perform('-', SinOsc.ar(59.563934, 3.770235, 0.288490, -0.063430).range(-0.026432514190674, -0.73682880401611))*EnvGen.kr(#[ 0, 2, 1, -99, 1, 0.01, 5, -4, 0, 0.02, 5, -4 ], gate, amp, 0, 1, 2))))}).play)

GENOME:
[ 0.87658286094666, 0.13238632678986, 0.20194101333618, 0.81867587566376, 0.87399089336395, 0.60985088348389, 0.89901769161224, 0.27304971218109, 0.3851375579834, 0.6437246799469, 0.17010962963104, 0.31229841709137, 0.22293603420258, 0.56832695007324, 0.26486468315125, 0.25054693222046, 0.53271245956421, 0.048996567726135, 0.4028148651123, 0.7767539024353 ]

PHENOME:
(SynthDef('RedGAPhenomeDef_101128_015559', {|out= 0, amp= 1, gate= 1| Out.ar(out, Limiter.ar(LeakDC.ar(LFPar.ar(LFClipNoise.ar(6071.557417, LFCub.kr(15.052203, 1.351127, Vibrato.kr(SyncSaw.kr(6.128215).exprange(1.0750316977501, 8.1160158157349)).range(0.26486468315125, 0.25054693222046)).range(0.6437246799469, 0.17010962963104)).exprange(12204.820652008, 17982.373478413)).range(-0.59611797332764, 0.63735175132751)*EnvGen.kr(#[ 0, 2, 1, -99, 1, 0.01, 5, -4, 0, 0.02, 5, -4 ], gate, amp, 0, 1, 2))))}).play)

GENOME:
[ 0.34140014648438, 0.30175685882568, 0.0056966543197632, 0.80570125579834, 0.04620635509491, 0.73648166656494, 0.52050232887268, 0.045454144477844, 0.40112888813019, 0.80093693733215, 0.16607248783112, 0.6278703212738, 0.67780292034149, 0.21197652816772, 0.92777013778687, 0.5347558259964, 0.61497759819031, 0.93128263950348, 0.67022502422333, 0.17048060894012 ]

PHENOME:
(SynthDef('RedGAPhenomeDef_101128_015602', {|out= 0, amp= 1, gate= 1| Out.ar(out, Limiter.ar(LeakDC.ar(SyncSaw.ar(52.612358, LFTri.ar(LFPulse.kr(PinkNoise.kr(0.927770, 0.069512).exprange(13.588278114796, 4.3183329105377)).exprange(16022.720007896, 3338.1283068657)).exprange(10419.636530876, 928.17380666733)).range(-0.98860669136047, 0.61140251159668).perform('sqrsum', Blip.ar.range(0.34045004844666, -0.65903878211975))*EnvGen.kr(#[ 0, 2, 1, -99, 1, 0.01, 5, -4, 0, 0.02, 5, -4 ], gate, amp, 0, 1, 2))))}).play)

GENOME:
[ 0.21759104728699, 0.19168221950531, 0.96565890312195, 0.2745361328125, 0.71634376049042, 0.31953656673431, 0.075420379638672, 0.73166418075562, 0.21231842041016, 0.14699053764343, 0.48020792007446, 0.18293488025665, 0.93624556064606, 0.65213680267334, 0.49147021770477, 0.69864809513092, 0.49048376083374, 0.61236214637756, 0.0073232650756836, 0.21936917304993 ]

PHENOME:
(SynthDef('RedGAPhenomeDef_101128_015605', {|out= 0, amp= 1, gate= 1| Out.ar(out, Limiter.ar(LeakDC.ar(LFTri.ar(LFPar.ar(LFTri.ar(551.637467, 0.731740, 0.936246, 0.304274).exprange(4262.1220397949, 2956.8709421158)).exprange(6404.3406033516, 1526.8991851807)).range(0.9313178062439, -0.450927734375).perform('ring4', LFDNoise1.ar(21.037774, 0.219369).range(-0.01903247833252, 0.22472429275513))*EnvGen.kr(#[ 0, 2, 1, -99, 1, 0.01, 5, -4, 0, 0.02, 5, -4 ], gate, amp, 0, 1, 2))))}).play)

GENOME:
[ 0.34140014648438, 0.30175685882568, 0.0056966543197632, 0.80570125579834, 0.04620635509491, 0.73648166656494, 0.52050232887268, 0.045454144477844, 0.40112888813019, 0.80093693733215, 0.16607248783112, 0.6278703212738, 0.67780292034149, 0.21197652816772, 0.92777013778687, 0.5347558259964, 0.61497759819031, 0.93128263950348, 0.67022502422333, 0.17048060894012 ]

PHENOME:
(SynthDef('RedGAPhenomeDef_101128_015608', {|out= 0, amp= 1, gate= 1| Out.ar(out, Limiter.ar(LeakDC.ar(SyncSaw.ar(52.612358, LFTri.ar(LFPulse.kr(PinkNoise.kr(0.927770, 0.069512).exprange(13.588278114796, 4.3183329105377)).exprange(16022.720007896, 3338.1283068657)).exprange(10419.636530876, 928.17380666733)).range(-0.98860669136047, 0.61140251159668).perform('sqrsum', Blip.ar.range(0.34045004844666, -0.65903878211975))*EnvGen.kr(#[ 0, 2, 1, -99, 1, 0.01, 5, -4, 0, 0.02, 5, -4 ], gate, amp, 0, 1, 2))))}).play)

GENOME:
[ 0.21759104728699, 0.19168221950531, 0.62269937992096, 0.2745361328125, 0.71634376049042, 0.31953656673431, 0.075420379638672, 0.73166418075562, 0.21231842041016, 0.69631904363632, 0.48020792007446, 0.18293488025665, 0.13190184533596, 0.65213680267334, 0.49147021770477, 0.14417177438736, 0.49048376083374, 0.61236214637756, 0.0073232650756836, 0.21936917304993 ]

PHENOME:
(SynthDef('RedGAPhenomeDef_101128_015625', {|out= 0, amp= 1, gate= 1| Out.ar(out, Limiter.ar(LeakDC.ar(LFTri.ar(LFPar.ar(LFTri.ar(551.637467, 0.731740, 0.131902, 0.304274).exprange(4262.1220397949, 13932.454491854)).exprange(6404.3406033516, 1526.8991851807)).range(0.24539875984192, -0.450927734375).perform('ring4', LFPar.ar(21.037774, 0.438738).range(-0.01903247833252, 0.22472429275513))*EnvGen.kr(#[ 0, 2, 1, -99, 1, 0.01, 5, -4, 0, 0.02, 5, -4 ], gate, amp, 0, 1, 2))))}).play)

annette

clean-up #12:

this is a live patch for 4-channel sound written for a friend some time ago. to fully function it expects that you have an external soundcard with 4 inputs (mics) and 4 outputs (speakers) connected. the idea is to be able to route the sound from any of the inputs to any of the outputs and do all kinds of crossfades and mixes in between.
there are two areas with sliders that are used to do the 4x4 routing. one can also record the resulting quad output to disk and play it back, or use previously recorded quad files. there is also a sampler with 28 voices that can be triggered with the computer keyboard (keys A-Z). additional features include midi learn (controllers) plus a preset system that can read and write to disk. keys '0-9' with and without capslock recall presets while shiftclicking one of the preset buttons in the gui stores the current setting. with the lag time one can make all the changes smooth - it interpolates over x seconds.

attached is the complete code for the program. sorry for the ugly interface.
it has been tested on sc3.4 on a mac with both cocoa and swingosc and should run cross-platform.

update 110315: added midi mappings load and save (version09)
update 111114: increased from 4 to 6 outputs and made the number of inputs/outputs flexible (version10)

AttachmentSize
File annette10.rtf30.02 KB

2-bit computer sonification

clean-up #11:

as a continuation of the n-bit computers story, here are three snippets of supercollider code that sonifies all possible programs of a little 2-bit computer emulator. they all map the sound differently. note that here i let load and stor instructions increase the program counter one extra step.
rendered mp3s for corresponding code are attached below.

//2-bit computer
//sonification #1 - all possible programs 0-255 in order, 16 ticks each
(
s.waitForBoot{
        var mem;
        var pc= 0;                                                      //program counter
        var reg= 0;                                                     //register
        var format= {|x| x.collect{|x| x.asBinaryString(2)}};
        var rd= {|x| mem@@x};
        var wr= {|x, y| mem.put(x%mem.size, y)};
       
        var snd;
        SynthDef(\snd, {|amp= 0, freq= 400, width= 0.5, pan= 0|
                var src= LFPulse.ar(freq, 0, width, amp.lag(0, 0.1));
                Out.ar(0, Pan2.ar(FreeVerb.ar(src, 0.2, 0.3), pan));
        }).send(s);
        s.sync;
        snd= Synth(\snd);
       
        Routine.run{
                4.do{|m0|
                4.do{|m1|
                4.do{|m2|
                4.do{|m3|
                        var op, running= true;
                        mem= [m3, m2, m1, m0];  //reordering here makes slightly different pieces
                        pc= 0;
                        reg= 0;
                        "".postln;
                        16.do{
                                if(running, {
                                        op= mem[pc];
                                        snd.set(\amp, 0.7, \freq, op.linexp(0, 3, 120, 1200), \pan, pc/3*2-1, \width, reg/3*0.8+0.1);
                                        switch(op,
                                                2r00, {running= false},//halt
                                                2r01, {reg= rd.(pc+1); pc= pc+1},//load  next addy into reg
                                                2r10, {reg= reg+1%4},//incr  reg
                                                2r11, {wr.(rd.(pc+1), reg); pc= pc+1}//stor  at addy in next byte
                                        );
                                        pc= pc+1%mem.size;
                                        [\pc, pc, \reg, reg, \mem, format.value(mem)].postln;
                                });
                                0.01.wait;
                                snd.set(\amp, 0);
                                0.001.wait;
                        };
                };};};};
                1.wait;
                snd.free;
        };
};
)

 


 

//2-bit computer
//sonification #2 - all possible programs 0-255 in order, maximum 100 ticks
(
s.waitForBoot{
        var mem;
        var pc= 0;                                                      //program counter
        var reg= 0;                                                     //register
        var format= {|x| x.collect{|x| x.asBinaryString(2)}};
        var rd= {|x| mem@@x};
        var wr= {|x, y| mem.put(x%mem.size, y)};
       
        var snd;
        SynthDef(\snd, {|amp= 0, freq= 400, width= 0.5, mod= 0, t_trig= 0, pan= 0|
                var env= EnvGen.ar(Env.perc(0.01, 0.1, 1, 0), t_trig);
                var src= SinOsc.ar(mod, SinOsc.ar(freq, 0, 2pi*width), amp);
                Out.ar(0, Pan2.ar(src*env, pan));
        }).send(s);
        s.sync;
        snd= Synth(\snd, [\amp, 0.7]);
       
        Routine.run{
                var cnt= 0;
                4.do{|m0|
                4.do{|m1|
                4.do{|m2|
                4.do{|m3|
                        var i= 0, op, running= true;
                        mem= [m0, m1, m2, m3];  //reordering here makes slightly different pieces
                        pc= 0;
                        reg= 0;
                        while({running and:{i<100}}, {
                                op= mem[pc];
                                snd.set(\t_trig, 1, \freq, op.linexp(0, 3, 120, 1200), \pan, pc/3*2-1, \width, pc+1, \mod, pc/3);
                                switch(op,
                                        2r00, {running= false},//halt
                                        2r01, {reg= rd.(pc+1); pc= pc+1},//load  next addy into reg
                                        2r10, {reg= reg+1%4},//incr  reg
                                        2r11, {wr.(rd.(pc+1), reg); pc= pc+1}//stor  at addy in next byte
                                );
                                pc= pc+1%mem.size;
                                i= i+1;
                                0.008.wait;
                        });
                        ("program:"+cnt).postln;
                        cnt= cnt+1;
                };};};};
                1.wait;
                snd.free;
        };
};
)

 


 

//2-bit computer
//sonification #3 - all possible programs 0-255 in order, 4 voices with freq from memory
(
s.waitForBoot{
        var mem;
        var pc= 0;                                                      //program counter
        var reg= 0;                                                     //register
        var format= {|x| x.collect{|x| x.asBinaryString(2)}};
        var rd= {|x| mem@@x};
        var wr= {|x, y| mem.put(x%mem.size, y)};
       
        var snds;
        SynthDef(\snd, {|amp= 0, freq= 400, width= 0.5, mod= 0, pm= 0, pan= 0|
                var src= SinOsc.ar(mod, SinOsc.ar(freq, SinOsc.ar(pm, 0, 2pi), 2pi*width), amp);
                Out.ar(0, Pan2.ar(src, pan));
        }).send(s);
        s.sync;
        snds= {Synth(\snd)}.dup(4);
       
        Routine.run{
                var cnt= 0;
                4.do{|m0|
                4.do{|m1|
                4.do{|m2|
                4.do{|m3|
                        var i= 0, op, running= true;
                        mem= [m3, m2, m1, m0];  //reordering here makes slightly different pieces
                        pc= 0;
                        reg= 0;
                        while({running and:{i<16}}, {
                                op= mem[pc];
                                switch(op,
                                        2r00, {running= false},//halt
                                        2r01, {reg= rd.(pc+1); pc= pc+1},//load  next addy into reg
                                        2r10, {reg= reg+1%4},//incr  reg
                                        2r11, {wr.(rd.(pc+1), reg); pc= pc+1}//stor  at addy in next byte
                                );
                                pc= pc+1%mem.size;
                                4.do{|r|
                                        snds[r].set(
                                                \amp, running.binaryValue*0.3,
                                                \freq, mem[r].linexp(0, 3, 300-cnt, 300+cnt),
                                                \pan, r/3*2-1,
                                                \width, op+1,
                                                \mod, reg/2,
                                                \pm, pc+1
                                        );
                                        0.01.wait;
                                };
                                i= i+1;
                        });
                        ("program:"+cnt).postln;
                        cnt= cnt+1;
                };};};};
                1.wait;
                snds.do{|x| x.free};
        };
};
)

 


n-bit computers

clean-up #10:

some 3 years ago i was really into writing code in supercollider to emulate/study/sonify various computer systems. here's a short write-up...

a 1-bit computer can only have two states. in the following code such a simple computer is emulated. the two possible opcodes 0 and 1 are set to take some basic actions: 0 will stop the program and 1 will post 'hello'.
there are only a total of four programs possible for this computer. with the line ~mem= [0, 0] we can load the first program [0, 0] into memory. that program means halt, halt and when we run it it will make the emulated computer stop right away. the second program [0, 1] will also stop instantaneously, but program three [1, 0] will post once and then halt. program four [1, 1] in turn will post forever and never halt.

//--all 1bit computer programs
~mem= [0, 0];           //program: halt halt - quits right away
~mem= [0, 1];           //program: halt post - also quits right away
~mem= [1, 0];           //program: post halt - posts hello and then quits
~mem= [1, 1];           //program: post post - posts hello forever

(
//--1bit computer.  load a program into memory before run
var pc= 0;                                                      //program counter
var reg= 0;                                                     //register (unused)
Routine.run{
        var op, running= true;
        while({running}, {
                op= ~mem[pc];
                switch(op,
                        0, {running= false},            //halt
                        1, {"hello".postln}             //post
                );
                pc= pc+1%~mem.size;
                [\pc, pc, \reg, reg, \mem, ~mem].postln;
                0.2.wait;
        });
        ("finished with mem="+~mem).postln;
};
""
)

if we take the same simple principle and implement a 2-bit computer, there are four possible opcodes and 256 different programs we can run (if we just fill the memory with unique combinations and call them programs - not many of these will make sense of course). the actions that the emulator below can take are simple (other actions are of course possible - here just a few standard ones)... halt - stops execution, load will take the next byte in the program and put it into the register, incr will increase the register by one and stor writes the content of the register back into the memory position (address) that is found in the next byte.
most programs will be pointless (at least for the actions implemented here), so i only list five out of 256 programs. so the first program [2r10, 2r10, 2r10, 2r00] will increment the register three times and then stop. program two increments the register, stores that at address 0 and stops. program three will first load 2r10 into the register, then increment the register twice and stop. the fourth program first loads 2r10 into the register, then increment the register, then stores the register at address 0 and last stops.
so far all the programs have been dull and pointless. but not program five! here we start to see the potential as it modifies itself as it runs. program five starts by incrementing the register, then it stores the register at address 3, then again it stores the register but at address 1! (note: here the trick is first noticed - although the original program said store at address 0, in the beginning of the program we overwrote that with address 1), then it loads 2r10 (note: here it wraps around and finds the value at address 0) into the register, then increment the register, then load 2r11 into the register, then store the register at address 1, then load 2r10 into the register, then increase the register, then store the register intro address 3, etc etc. until it finally halts after 16 cycles. because of the self-modifying program code the execution is long and complex to follow. very interesting i find.

//--selected 2bit computer programs
~mem= [2r10, 2r10, 2r10, 2r00]; //program: incr incr incr halt
~mem= [2r10, 2r11, 2r00, 2r00]; //program: incr stor halt halt
~mem= [2r01, 2r10, 2r10, 2r00]; //program: load incr incr halt
~mem= [2r01, 2r10, 2r11, 2r00]; //program: load incr stor halt
~mem= [2r10, 2r11, 2r11, 2r00]; //program: incr stor stor halt

(
//--2bit computer.  load a program into memory before run
var pc= 0;                                                      //program counter
var reg= 0;                                                     //register
var format= {|x| x.collect{|x| x.asBinaryString(2)}};
var rd= {|x| ~mem@@x};
var wr= {|x, y| ~mem.put(x%~mem.size, y)};
Routine.run{
        var op, running= true;
        while({running}, {
                op= ~mem[pc];
                switch(op,
                        2r00, {running= false},         //halt
                        2r01, {reg= rd.(pc+1)},         //load  next addy into reg
                        2r10, {reg= reg+1%4},           //incr  reg
                        2r11, {wr.(rd.(pc+1), reg)}     //stor  at addy in next byte
                );
                pc= pc+1%~mem.size;
                [\pc, pc, \reg, reg, \mem, format.value(~mem)].postln;
                0.2.wait;
        });
        ("finished with mem="+format.value(~mem)).postln;
};
""
)

one variation of the emulator code above (and also for the following 3-4bit computers) would be to let the load and stor instructions also increase the program counter (pc) by 1. so it takes the following byte as an argument and then skips over that for the next cycle. this would be a more common way to implement instructions like load and stor (i do it in the last example - the 8bit computer).

so if a 2-bit computer can start to mutate its code as it runs, imagine what a 3-bit computer is capable of. 16777216 different programs (8.pow(8)) are possible, 8 opcodes and actions and 8 bytes of memory. the actions implemented in the 3-bit emulator below includes and/or operations as well as a jump instruction. all the example programs are pretty lame. e.g. program four makes an endless loop jumping back and forth. one could write a lot more interesting programs.

//--selected 3bit computer programs
~mem= [2r010, 2r011, 2r111, 2r000, 2r010, 2r011, 2r111, 2r000]; //program: incr stor post halt incr stor post halt
~mem= [2r101, 2r111, 2r110, 2r111, 2r011, 2r000, 2r000, 2r000]; //program: and  post or   post stor halt halt halt
~mem= [2r100, 2r101, 2r000, 2r000, 2r000, 2r111, 2r000, 2r000]; //program: jump and  halt halt halt post halt halt
~mem= [2r100, 2r101, 2r000, 2r000, 2r000, 2r100, 2r000, 2r000]; //program: jump and  halt halt halt jump halt halt

(
//--3bit computer.  load a program into memory before run
var pc= 0;                                                              //program counter
var reg= 0;                                                             //register
var format= {|x| x.collect{|x| x.asBinaryString(3)}};
var rd= {|x| ~mem@@x};
var wr= {|x, y| ~mem.put(x%~mem.size, y)};
Routine.run{
        var op, running= true;
        while({running}, {
                op= ~mem[pc];
                switch(op,
                        2r000, {running= false},                //halt
                        2r001, {reg= rd.(pc+1)},                //load  next addy into reg
                        2r010, {reg= reg+1%8},                  //incr  reg
                        2r011, {wr.(rd.(pc+1), reg)},   //stor  at addy in next byte
                        2r100, {pc= rd.(pc+1)-1},               //jump  to addy in reg
                        2r101, {reg= rd.(pc+1)&reg},    //and   reg with next addy
                        2r110, {reg= rd.(pc+1)|reg},    //or    reg with next addy
                        2r111, {reg.postln}                     //post  reg
                );
                pc= pc+1%~mem.size;
                [\pc, pc, \reg, reg, \mem, format.value(~mem)].postln;
                0.2.wait;
        });
        ("finished with mem="+format.value(~mem)).postln;
};
""
)

with a 4-bit computer it starts to get difficult to come up with relevant actions to take for all the 16 opcodes. in the emulator below there is a stack with push and pull opcodes, more math with addition, subtraction and left/right shifts as well as inversion of bits. there is also a random instruction. again the example programs are really dull - it is possible to write much more interesting programs or why not generate programs at random and see which ones make interesting results? like this... ~mem= {16.rand}.dup(16);

//--selected 4bit computer programs
~mem= [2r0010, 2r0010, 2r0011, 2r1111, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000];
~mem= [2r0001, 2r1110, 2r0010, 2r1111, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000, 2r0000];

(
//--4bit computer.  load a program into memory before run
var pc= 0;                                                                              //program counter
var sp= 15;                                                                             //stack pointer
var reg= 0;                                                                             //register
var format= {|x| x.collect{|x| x.asBinaryString(4)}};
var rd= {|x| ~mem@@x};
var wr= {|x, y| ~mem.put(x%~mem.size, y)};
Routine.run{
        var op, running= true;
        while({running}, {
                op= ~mem[pc];
                switch(op,
                        2r0000, {running= false},                               //halt
                        2r0001, {reg= rd.(pc+1)},                               //load  next addy into reg
                        2r0010, {reg= reg+1%16},                                //incr  reg
                        2r0011, {wr.(rd.(pc+1), reg)},                  //stor  at addy in next byte
                        2r0100, {pc= rd.(pc+1)-1},                              //jump  to addy in reg
                        2r0101, {reg= rd.(pc+1)&reg},                   //and   reg with next addy
                        2r0110, {reg= rd.(pc+1)|reg},                   //or    reg with next addy
                        2r0111, {reg.postln},                                   //post  reg
                        2r1000, {wr.(rd.(pc+1), rd.(pc+1).bitXor)},//inv  at addy in next byte
                        2r1001, {wr.(rd.(pc+1), 16.rand)},              //rand  at addy in next byte
                        2r1010, {reg= reg.leftShift(1)},                        //lsr   reg
                        2r1011, {reg= reg.rightShift(1)},               //rsr   reg
                        2r1100, {reg= reg+rd.(pc+1)%16},                        //add   reg val at addy in next byte
                        2r1101, {reg= reg-rd.(pc+1)%16},                        //sub   reg val at addy in next byte
                        2r1110, {wr.(sp, reg); sp= sp-1},               //push  reg to stack
                        2r1111, {reg= rd.(sp+1%16); sp= (sp+1).min(15)}//pull to reg from stack
                );
                pc= pc+1%~mem.size;
                [\pc, pc, \reg, reg, \sp, sp, \mem, format.value(~mem)].postln;
                0.2.wait;
        });
        ("finished with mem="+format.value(~mem)).postln;
};
""
)

things are getting really tricky when implementing an 8-bit computer from scratch. in the example below i only implemented 19 out of the 256 possible opcodes. for the actions/instructions set i took inspiration from the 6502 microprocessor. if you know some assembly you should recognise what the opcodes do. the stack pointer is commented out because none of the 19 active opcodes uses it.

~mem= 0.dup(256); ""            //clear memory

//--selected 8bit computer programs
//ADC, #0x0F, AND, #0x0A, ORA, #0x0F, EOR, #0x10, BRK
 [0x69, 0x0F, 0x29, 0x0A, 0x09, 0x0F, 0x49, 0x0A, 0x00].do{|x, i| ~mem.put(i, x)};

//LDA, #0xFF, STA, #0x20, BRK
 [0xA9, 0xFF, 0x85, 0x20, 0x00].do{|x, i| ~mem.put(i, x)};

//INC,  0x20, NOP,  DEC,  0x21, BRK
 [0xE6, 0x20, 0xEA, 0xC6, 0x21, 0x00].do{|x, i| ~mem.put(i, x)};

//CLC,  BCS, #0x07, SEC,  BCS, #0x07, BRK,  NOP
 [0x18, 0xB0, 0x07, 0x38, 0xB0, 0x07, 0x00, 0xEA].do{|x, i| ~mem.put(i, x)};

(
//--8bit computer.  load a program into memory before run
//implemented some of the 6502 opcodes and some of its flags
var pc= 0x00;                                                                           //program counter
//var sp= 0xFF;                                                                 //stack pointer
var r= 0x00;                                                                            //register
var c= 0x00;                                                                            //carry
var format= {|x| x.collect{|x| x.asHexString(2)}};
var rd= {|x| ~mem@@x};
var wr= {|x, y| ~mem.put(x%~mem.size, y)};
Routine.run{
        var op, running= true;
        while({running}, {
                op= ~mem[pc];
                switch(op,
                        0x65, {r= r+rd.(rd.(pc+1))+c%0x100; pc= pc+1},//ADC aa
                        0x69, {r= r+rd.(pc+1)+c%0x100; pc= pc+1},       //ADC #aa
                        0x29, {r= r&rd.(pc+1); pc= pc+1},               //AND #aa
                        0x25, {r= r&rd.(rd.(pc+1)); pc= pc+1},  //AND aa
                        0x90, {if(c==0, {pc= rd.(pc+1)-1}, {pc= pc+1})},//BCC aa
                        0xB0, {if(c==1, {pc= rd.(pc+1)-1}, {pc= pc+1})},//BCS aa
                        0x00, {running= false},                                 //BRK
                        0x18, {c= 0},                                                   //CLC
                        0xC6, {wr.(rd.(pc+1), rd.(rd.(pc+1))-1%0x100); pc= pc+1},//DEC aa
                        0x49, {r= r.bitXor(rd.(pc+1)); pc= pc+1},       //EOR #aa
                        0x45, {r= r.bitXor(rd.(rd.(pc+1))); pc= pc+1},//EOR aa
                        0xE6, {wr.(rd.(pc+1), rd.(rd.(pc+1))+1%0x100); pc= pc+1},//INC aa
                        0xA9, {r= rd.(pc+1); pc= pc+1},                 //LDA #aa
                        0xA5, {r= rd.(rd.(pc+1)); pc= pc+1},            //LDA aa
                        0xEA, {'nop'.postln},                                   //NOP
                        0x09, {r= r|rd.(pc+1); pc= pc+1},               //ORA #aa
                        0x05, {r= r|rd.(rd.(pc+1)); pc= pc+1},  //ORA aa
                        0x38, {c= 1},                                                   //SEC
                        0x85, {wr.(rd.(pc+1), r); pc= pc+1},            //STA aa
                        {("opcode not implemented"+op).warn}
                );
                pc= pc+1%~mem.size;
                [\pc, pc.asHexString(2), \r, r.asHexString(2), \c, c, \op, op.asHexString(2)].postln;
                0.2.wait;
        });
        ("finished with mem="+format.value(~mem)).postln;
};
""
)

and here's a dissassembler for the emulator above. it will just dump the whole memory (256 bytes) and post which opcode is stored at what address.

(
//--8bit computer dissassembler
var res= [], adr= [0];
var putImmediate= {|op, val| res= res++[op+"#"++val.asHexString(2)]};
var putAbsolute= {|op, val| res= res++[op+"$"++val.asHexString(2)]};
var putOpcode= {|op| res= res++[op]};
var i= 0;

while({i<~mem.size}, {
        switch(~mem[i],
                0x65, {putAbsolute.("ADC", ~mem[i+1]); i= i+2},
                0x69, {putImmediate.("ADC", ~mem[i+1]); i= i+2},
                0x29, {putImmediate.("AND", ~mem[i+1]); i= i+2},
                0x25, {putAbsolute.("AND", ~mem[i+1]); i= i+2},
                0x90, {putAbsolute.("BCC", ~mem[i+1]); i= i+2},
                0xB0, {putAbsolute.("BCS", ~mem[i+1]); i= i+2},
                0x00, {putOpcode.("BRK"); i= i+1},
                0x18, {putOpcode.("CLC"); i= i+1},
                0xC6, {putAbsolute.("DEC", ~mem[i+1]); i= i+2},
                0x49, {putImmediate.("EOR", ~mem[i+1]); i= i+2},
                0x45, {putAbsolute.("EOR", ~mem[i+1]); i= i+2},
                0xE6, {putAbsolute.("INC", ~mem[i+1]); i= i+2},
                0xA9, {putImmediate.("LDA", ~mem[i+1]); i= i+2},
                0xA5, {putAbsolute.("LDA", ~mem[i+1]); i= i+2},
                0xEA, {putOpcode.("NOP"); i= i+1},
                0x09, {putImmediate.("ORA", ~mem[i+1]); i= i+2},
                0x05, {putAbsolute.("ORA", ~mem[i+1]); i= i+2},
                0x38, {putOpcode.("SEC"); i= i+1},
                0x85, {putAbsolute.("STA", ~mem[i+1]); i= i+2},
                {("opcode not implemented"+~mem[i]).warn; i= i+1}
        );
        adr= adr.add(i);
});
"adr: op:\n-----------------".postln;
res.do{|x, i| (" $"++adr[i].asHexString(2)+x).postln};
""
)

i did write a full 6502 emulation that is in a working state. it is written as a class and takes up about 2000 lines of sc code. i will clean it up and publish it another day. i also did the CPU65C02 version that sits in the AppleIIc computer and the 6510 that is in the commodore64.

speak errors&warnings

clean-up #9:

here's a little tool originally made for visually impaired supercollider users. press the 'esc' key and the built-in computer voice will read the last error message posted. hold down 'alt' and press 'esc' and it will read the last warning message posted. if there are no errors or warnings it won't say anything.
this will only work on osx i'm afraid. put it in your startup.rtf document to make it to load by default.

//redFrik 2007
//find last posted error or warning and speak it using the built in computer voice (osx only).
//'esc'     - to speak last error
//'alt+esc' - to speak last warning
(
Document.globalKeyDownAction_{|doc, key, mod, code|
        var d, i;
        if(code==27, {                          //esc
                d= Document.listener.string;
                if(mod&524288==524288, {        //esc+alt
                        i= d.findBackwards("WARNING:");
                        if(i.notNil, {
                                ("warning"+(d.copyRange(i, i+200).split($\r)[1])).speak;
                        });
                }, {                                            //plain esc
                        i= d.findBackwards("ERROR:");
                        if(i.notNil, {
                                d.copyRange(i, i+100).split($\r)[0].speak;
                        });
                });
                ""
        });
}
)

/*
//--test
OOO.new
1.asd
1\2
11
s.quit
{SinOsc.ar}.play        //warns about localhost not running
*/

newscoolish

clean-up #8:

this is a port of a pd patch by martin brinkmann that in turn is a port of a reaktor ensemble by lazyfish. i wrote this sc patch in 2006 and recently revisited the code, added a preset system and made it run under the current supercollider version 3.4.
though there are a lot of things that could be improved. the graphical interface is particularly rough and ugly.

attached is the code and a short mp3 with example sounds.


AttachmentSize
Package icon newscoolish1+.zip13.13 KB

RedBMP

clean-up #7:

today i cleaned up and finished a class belonging to my redSys quark. the class can read and write bitmap images (.bmp files). to get it run the following command in supercollider...

Quarks.checkoutAll;

and then install redSys via the gui...

Quarks.gui;

it's a fairly slow and useless class but well, it can generate images relatively fast. and saving to disk is also quite quick. but avoid drawing the images to screen. the built in makeWindow method is very slow as it goes through width*height number of pixels and draws a rectangle for each using Pen.

(
b= RedBMP(640, 480, 32);
b.data= {|i|
        var x= i%b.width;
        Color.red(sin(x/b.width*2pi+sin(x*0.11)), sin(i*0.01+sin(x*0.1)).abs);
}.dup(b.width*b.height);
b.write("~/Desktop/redbmp.bmp");
)

this code outputs the following file (notice the alpha channel)...
1.2mb windows v3 bmp, 640x480, 72dpi

a script that rules the world

clean-up #6:

this is a hack from 2007. it was done in cocoacollider, but now i updated it using the standard SCNSObject in supercollider. it works by talking to google earth via applescript.

//redFrik 2007 - updated 2010
//req. mac osx, google earth

//--init
(
s.waitForBoot{
        SynthDef(\redGoogleKick, {|out= 0, t_trig= 1, freq= 50, dur= 0.5, envFreq= 500, envAtk= 0.005, envRel= 0.1, amp= 1|
                var z, env, fenv, click;
                env= EnvGen.ar(Env.linen(0, 0, dur, amp), t_trig);
                fenv= EnvGen.kr(Env([envFreq, envFreq-freq*0.275+freq, freq], [envAtk, envRel]), t_trig);
                click= EnvGen.ar(Env.linen(0, 0, 0), t_trig);
                z= SinOsc.ar(fenv, 0, env, click);
                Out.ar(out, z);
        }).add;
        SynthDef(\redGoogleSynth, {|out= 0, t_trig= 1, freq= 200, atk= 0.005, dec= 0.5, pm= 0.5, amp= 1|
                var z, env, pmod;
                env= EnvGen.kr(Env.perc(atk, dec, 1, -3), t_trig);
                pmod= LFSaw.ar(freq*2.pow(TIRand.kr(1, 3, t_trig)), 0, env*pm);
                z= SinOsc.ar(freq, pmod, env);
                Out.ar(out, z*amp);
        }).add;
        SynthDef(\redGoogleDelay, {|in= 0, out= 0, depth= 3, time= 0.45, amp= 1, dist= 2, pan= 0|
                var src, z;
                src= In.ar(in, 1);
                z= CombL.ar(src, 1.5, time.min(1.5), depth, amp, src);
                Out.ar(out, Pan2.ar(Clip.ar(z*dist, -1, 1), pan));
        }, #[\ir, \ir, 0, 0.5, 0, 0, 0]).add;
};
f= {|cmd|
        {
                var o;
                o= SCNSObject("NSAppleScript", "initWithSource:", ["tell application \"Google Earth\"\n"++cmd++"\nend tell"]);
                o.invoke("executeAndReturnError:", [nil], true);
                o.release;
        }.defer;
};
f.value("open");
)

//--run (wait until localhost and googlearth started, also disable all overlays in googleearth)
(
f.value("SetViewInfo{latitude:0, longitude:0, distance:10000000, tilt:0, azimuth:0} speed 5");
d= Synth(\redGoogleDelay, [\out, 0, \in, 32]);
k= Synth(\redGoogleKick, [\t_trig, 0]);
e= Synth(\redGoogleSynth, [\out, 32, \t_trig, 0]);
Tdef(\redGoogle).play;
Tdef(\redGoogle, {
        var dist, xxx= 0, yyy= 0;
        300.do{|i|
                if(i%25==0, {
                        if(i%100==0, {
                                k.set(\t_trig, 1, \amp, 0.1, \out, 32, \freq, 2000, \dur, 1.5, \envAtk, 0.005, \envRel, 0.2);
                        });
                        e.set(\t_trig, 1, \freq, [500, 600, 700].wrapAt(i), \amp, 0.5, \dec, 0.5, \pm, 0.2);
                        if(i%4==0, {
                                xxx= xxx+1.0.rand2;
                                yyy= yyy+20.rand2;
                                f.value("SetViewInfo{latitude:"++1.0.rand2++", longitude:"++yyy++", distance:10000000, tilt:0, azimuth:0} speed 0.4");
                        });
                });
                (1/25).wait;
        };
        f.value("SetViewInfo{latitude:0, longitude:0, distance:10000000, tilt:0, azimuth:0} speed 0.5");
        300.do{|i|
                if(i%25==0, {
                        dist= [400, 600, 700, 800].wrapAt(i);
                        k.set(\t_trig, 1, \amp, 1, \out, 32, \freq, 50, \dur, 0.5, \envAtk, 0.005, \envRel, 0.1);
                        e.set(\t_trig, 1, \freq, dist, \amp, 0.5, \dec, 0.5, \pm, 1-(i/300));
                        f.value("SetViewInfo{latitude:0, longitude:0, distance:"++8000000++", tilt:"++45.rand++", azimuth:0} speed 5");
                }, {
                        if(i%25==1, {
                                f.value("SetViewInfo{latitude:0, longitude:"++([100, -100].choose)++", distance:"++5000000++", tilt:0, azimuth:10} speed 0.5");
                        });
                });
                (1/25).wait;
        };
        d.set(\dist, 4);
        f.value("SetViewInfo{latitude:52.45, longitude:-1.93, distance:300, tilt:0, azimuth:0} speed 0.5");
        50.do{|i|
                if(i%25==0, {
                        k.set(\t_trig, 1, \amp, 1, \out, 32, \freq, 50, \dur, 0.5, \envAtk, 0.005, \envRel, 0.1);
                        e.set(\t_trig, 1, \freq, [100, 500, 600, 700].wrapAt(i), \amp, 0.5, \dec, 1.1, \pm, 2);
                });
                (1/25).wait;
        };
        d.set(\dist, 5);
        500.do{|i|
                if(i%25==0, {
                        k.set(\t_trig, 1, \amp, 1, \out, 32, \freq, 50, \dur, 0.5, \envAtk, 0.005, \envRel, 0.1);
                        e.set(\t_trig, 1, \freq, [200, 500, 600, 700].wrapAt(i), \amp, 0.5, \dec, 1.1, \pm, 2);
                        f.value("SetViewInfo{latitude:52.45, longitude:-1.93, distance:"++200.rrand(300)++", tilt:"++(i%45)++", azimuth:"++(i%180)++"} speed 1");
                }, {
                        if(i%20==18, {
                                k.set(\t_trig, 1, \amp, 0.3, \out, [0, 1].choose, \freq, [2100, 100].wrapAt(i), \dur, 0.9, \envAtk, 0.1.linrand, \envRel, i%3);
                        })
                });
                (1/25).wait;
        };
})
)

f.value("quit");
/*
SCNSObject.dumpPool;
SCNSObject.freePool;
*/

//--some notes:

there are two ways to run applescripts from within supercollider. one is to construct a string with osascript -e and a unixCmd, the other is creating a cocoa NSAppleScript object with the SCNSObject cocoa binding class. the later method is slightly more involved but runs way faster.

the following two examples do the same thing - switches to Finder.

//--unixCmd version
"osascript -e 'tell application \"Finder\" to activate'".unixCmd
//--NSObject version
a= SCNSObject("NSAppleScript", "initWithSource:", ["tell application \"Finder\" to activate"]);
a.invoke("executeAndReturnError:", [nil], true);
a.release;

SCNSObject.dumpPool;    //should be 0
SCNSObject.freePool;     //free stray objects if any left

also i've attached a simple class that wraps the functionality. it's called RedEarth.

AttachmentSize
Package icon RedEarth.zip2.97 KB

Pages

Subscribe to RSS - supercollider