; CellularAutomata.csd
; Written by Iain McCurdy, 2015
; This example makes use of the vcella opcode to implement 1 dimensional cellular automata.
; For a more detailed explanation of cellular automata and the vcella opcode can be found in the Csound manual.
; It is also used to demonstrate the use of the gentable widget with GEN02 and binary values (only on or off)
; to create a button bank.
; If the function table is formed with a minus gen number (-2) then it can be edited through the GUI.
; The 'Initial States' table (red) and the 'Rule' (yellow) tables can be edited through the GUI.
; The 'Output States' table (green), formed with a positive gen number (2), cannot be edited and is for display only.
; The sonification employs the output table as if it were a looping step sequencer.
; Each step (cell) is associated with a particular pitch or tone so that we can here its repetition.
; These variations in tone, cell to cell, are randomly chosen but are fixed once set. They can be re-randomised by
; clicking 'Resend Initial State'
; The maximum group size is 64 cells
; The maximum rule length is 27 (bits)
; Trigger Change sets how the cellular automata algorithm will be triggered to apply a change
; --------------
; Trigger -- this button triggers an update manually
; Auto -- if this checkbox is active, triggers will be generated automatically by a metronome at a rate 'Rate'
; Rate -- the rate at which triggers will be generated if 'Auto' is active
; Sonification allows for some setting of how the 'Output States' table is sonified
; ------------
; Note Centre -- defines the central note of the distribution (MIDI note number)
; Note Range -- defines the range (+ and -) of the distribution of notes
; Duration -- defines the duration of notes
; Amplitude -- ampltude of note (there is some randomisation of amplitude in order the make the results slightly less mechanical)
; Free/Sync. (combobox) -- sets how the rate of the metronome for generation of notes is controlled
; if 'free' then rate is independent of the cell tranformation metronome and is defined by the adjacent 'Rate' numberbox
; if 'sync' then it is synced to the cell transformation metronome. In this mode the actual rate of note/rest generation will be: cell_rate x n_elements x repeats
; Rule defines the rule that will be applied to determine future statuses of cells in the output group
; ----
; Length -- length of the rule (the white bracket over the table indicates how many elements of the table will be used)
; Radius -- Number of cells (to the left and to the right) to include when applying the rule
; Elements -- Number of elements to apply the rule to when triggered (the white bracket over the input and output groups indicates how many elements of the tables will be used)
; Initial States defines the initial states (alive(on) / dead(off) of each of the 32 cells)
; --------------
; Resend Initial State -- pressing this button will reset the cellular group to its original state
; If cell evolution gets stuck (e.g. all on, all off or alternating between all on and all off)
; click this the reset.
; Output States is for the display of cells living (on) or dead (off) in the output group
; -------------
; Iterations -- this number box is for output only and displays the number of iterations we are away from the original state.
; A simple sonification is created by reading through the output cell group to play a rhythm (on=play a note, off=rest)
; Each cell is associated with a particular note.
form caption("Cellular Automata [vcella]"), size(830, 360),pluginid("CeAu"), guirefresh(64)
; Triggering
image bounds( 5, 5,205, 70), colour(0,0,0,0), outlinecolour("white"), outlinethickness(1), shape("sharp"), plant("Trigger") {
label bounds( 10, 5,100, 13), text("Trigger Change"), align("left")
button bounds( 10, 35, 60, 20), text("Manual","Manual"), channel("generate"), latched(0)
checkbox bounds( 80, 35, 50, 15), text("Auto"), channel("AutoGenerate"), value(1), colour("DarkSlateBlue")
numberbox bounds(135, 25, 60, 30), text("Rate"), channel("rate"), range(0,8,0.2,1,0.001)
}
; Sonification
image bounds(215, 5,610, 70), colour(0,0,0,0), outlinecolour("white"), outlinethickness(1), shape("sharp"), plant("Sonification") {
label bounds( 10, 5,100, 13), text("Sonification"), align("left")
checkbox bounds( 10, 35, 60, 15), text("On/Off"), channel("sonify"), value(1), colour("DarkSlateBlue")
numberbox bounds( 80, 25, 70, 30), text("Note Centre"), channel("NoteCentre"), range(0,127,100,1,1)
numberbox bounds(155, 25, 70, 30), text("Note Range"), channel("NoteRange"), range(0,72,64,1,1)
numberbox bounds(235, 25, 70, 30), text("Duration"), channel("NoteDur"), range(0.01,0.5,0.1,0.5,0.01)
numberbox bounds(310, 25, 70, 30), text("Amplitude"), channel("NoteAmp"), range(0,1,0.3,0.5,0.01)
combobox bounds(390, 35, 60, 20), text("Free","Sync."), channel("sync"), value(2)
numberbox bounds(455, 25, 70, 30), text("Rate"), channel("NRate"), range(1,72,8,1,1), identchannel("NRateID")
numberbox bounds(530, 25, 70, 30), text("Repeats"), channel("SyncRepeats"), range(1,16,4,1,1), identchannel("SyncRepeatsID")
}
; Rule
image bounds( 5, 80,820, 70), colour(0,0,0,0), outlinecolour("white"), outlinethickness(1), shape("sharp"), plant("RuleTable") {
label bounds( 10, 5,100, 13), text("Rule"), align("left")
numberbox bounds( 10, 20, 55, 30), text("Length"), channel("RuleLen"), range(1,27,8,1,1)
numberbox bounds( 75, 20, 55, 30), text("Radius"), channel("radius"), range(1,2,2,1,1)
image bounds(135, 20,200, 50), colour(30,30,30), outlinecolour("white"), outlinethickness(1), shape("sharp"), identchannel("RuleRange")
gentable bounds(135, 30,675, 25), tablenumber(3), tablecolour("GoldenRod"), amprange(0,1,3,1), zoom(-1), active(1)
}
; Initial State
image bounds( 5,155,820, 85), colour(0,0,0,0), outlinecolour("white"), outlinethickness(1), shape("sharp"), plant("InitTable") {
label bounds( 10, 5,100, 13), text("Initial States"), align("left")
image bounds( 10, 26,800, 32), colour(60,60,60), outlinecolour("white"), outlinethickness(1), shape("sharp"), identchannel("InputRange")
gentable bounds( 10, 35,800, 15), tablenumber(1), tablecolour("FireBrick"), amprange(0,1,1,1), zoom(-1), active(1)
button bounds( 10, 60,140, 20), text("Resend Initial State","Resend Initial State"), channel("reinit")
}
image bounds( 5,245,820, 95), colour(0,0,0,0), outlinecolour("white"), outlinethickness(1), shape("sharp"), plant("OutTable") {
label bounds( 10, 5,100, 13), text("Output States"), align("left")
image bounds( 10, 26,800, 32), colour(60,60,60), outlinecolour("white"), outlinethickness(2), shape("sharp"), identchannel("OutputRange")
gentable bounds( 10, 35,800, 15), tablenumber(2), tablecolour("green"), amprange(0,1,2,1), zoom(-1), identchannel("OutTable")
numberbox bounds( 10, 60, 60, 30), text("Elements"), channel("elements"), range(1,64,32,1,1)
numberbox bounds( 80, 60, 60, 30), text("Iterations"), channel("iter"), range(0,100000,0,1,1), active(0), textcolour(255,255,255), fontcolour(255,255,255)
}
label bounds(5,345,110,11), text("Iain McCurdy |2015|")
-dm0 -n
sr = 44100 ;SAMPLE RATE
ksmps = 32 ;NUMBER OF AUDIO SAMPLES IN EACH CONTROL CYCLE
nchnls = 2 ;NUMBER OF CHANNELS (1=MONO)
0dbfs = 1 ;MAXIMUM AMPLITUDE
seed 0
giInitTable ftgen 1,0, 64,-2,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0 ; initial states table
giOutTable ftgen 2,0, 64, 2,0 ; output states table
giRuleFunc ftgen 3,0,-27,-2,0,1,0,1,0,0,0,1 ,0,0,1,0,1,0,0,1, 0,0,1,1,0,1,0,0 ; rule function table
opcode TriggerToGatei,k,ki
ktrig,idur xin
kdlytrig delayk ktrig,idur
kgate samphold ktrig,ktrig + kdlytrig
xout kgate
endop
instr 1
kgenerate chnget "generate" ; manual trigger
kreinit chnget "reinit" ; reinitialise cell group trigger
kAutoGenerate chnget "AutoGenerate" ; metronome trigger on/off
kCRate chnget "rate" ; rate of cell metronome
ktrig trigger kgenerate,0.5,0 ; generate a 'bang' if manual trigger button (Trigger) is pressed
kReinitTrig trigger kreinit,0.5,0 ; generate a 'bang' if reinitialise cell group trigger button (Restart) is pressed
if kAutoGenerate==1 then ; if 'Auto' is 'on'...
kAutoTrig metro kCRate ; metronome
ktrig += kAutoTrig ; add metronome triggers to manual triggers
endif
kRuleLen chnget "RuleLen" ; rule length (in bits)
kRuleLen init 8 ; initial value for rule length (zero would cause a crash)
kradius chnget "radius" ; radius of test group each time rule is applied to a cell
kradius init 1 ; initial value for radius (zero would cause a crash)
kelements chnget "elements" ; number of elements to use (in the output group)
kelements init 32 ; initial value for number of elements (zero would cause a crash)
kiter init 0 ; number of iterations we are away from the initial state
if changed(kelements)==1 then ; if number of elements counter has been changed...
Smsg sprintfk "size(%d,33)",25*kelements*0.5 ; new length string
chnset Smsg,"InputRange" ; send to input group bracket
chnset Smsg,"OutputRange" ; send to output group bracket
endif
if changed(kRuleLen)==1 then ; if rule length counter has been changed...
Smsg sprintfk "size(%d,45)",25*kRuleLen ; new length string
chnset Smsg,"RuleRange" ; send to rule function bracket
endif
kIChange changed kRuleLen,kradius,kelements ;
if kIChange==1 then ; if any of these i-rate parameters has been changed
reinit UPDATE ; begin a reinitialisation pass from the label
endif
if kReinitTrig==1||kIChange==1 then ; if 'Restart' button has been pressed
kiter = 0 ; reset iterations counter
chnset kiter,"iter" ; send zero value to GUI counter
endif
UPDATE: ; reinitialise from here
vcella ktrig, kReinitTrig, giOutTable, giInitTable, giRuleFunc, i(kelements), i(kRuleLen), i(kradius)
if ktrig==1 then ; if a trigger has been generated...
kiter += 1 ; ...increment iterations counter
chnset kiter,"iter" ; send new value to counter
chnset "tablenumber(2)","OutTable" ; update table display of cell output states
endif
; SONIFICATION
ksonify chnget "sonify"
if ksonify==1 then
if kReinitTrig==1 then
reinit NEW_RANDOM_ROW
endif
NEW_RANDOM_ROW:
girnd ftgen 999,0,128,21,1 ; set random distribution (32 uniform random values in the range 0-1)
rireturn
ksync chnget "sync" ; sync to cell metronome
ksync init 2
if changed(ksync)==1 then
if ksync==1 then
chnset "visible(1)","NRateID"
chnset "visible(0)","SyncRepeatsID"
else
chnset "visible(1)","NRateID"
chnset "visible(0)","SyncRepeatsID"
endif
endif
if ksync==2&&kAutoGenerate==1 then ; sync to cell metronome (only also if it as running)
if ktrig==1 then ; resync note metronome to cell metronome
reinit RESET_METRO
endif
RESET_METRO:
iSyncRepeats chnget "SyncRepeats"
kNTrig metro kCRate*iSyncRepeats*kelements ; mstronome synced to cell metronome
rireturn
else ; free metronome
kNRate chnget "NRate" ; note rate (notes per second)
kNTrig metro kNRate ; metronome to trigger notes
endif
kstep init 0 ; initial step
if kNTrig==1 then ; if metronome trigger is received
kact table kstep,giOutTable ; read cell state 1=alive 0=dead
if kact==1 then ; if 'alive'
kNoteDur chnget "NoteDur"
event "i",2,0,kNoteDur,kstep ; play a note
endif
kstep wrap kstep+1,0,kelements ; increment step counter
endif
endif
endin
instr 2 ; sonification
iRndAmp random 0.5,1 ; random amplitude (to make sonification a little less mechanical)
iAmp chnget "NoteAmp"
iNC chnget "NoteCentre"
iNR chnget "NoteRange"
irnd table p4,girnd ; read fixed random value for this cell
inote limit iNC + ((irnd-0.5)*iNR),0,127 ; note number (offset + random offset). Limited 0 - 127.
p3 = p3 * octave((52-inote)/24) ; scale duration according to note number. (Higher notes shorter, lower notes longer)
aenv expon 1,p3,0.0001 ; amplitude envelope
acps expon cpsmidinn(inote),p3,cpsmidinn(inote)/8 ; glissando
asig poscil (aenv-0.0001)*iAmp*iRndAmp,acps ; audio oscillator
asig *= poscil:a(1,100) ; ring modulate
ipan random 0.4,0.6 ; random pan value (to make sonification a little less mechanical)
aL,aR pan2 asig,ipan ; stereo panned
outs aL,aR ; send audio to outputs
endin
i 1 0 [3600*24*7]