'****************************************************************************** 'Subject: 1-D continuous-valued cellular automata: ' model of two complex vibrating strings. 'Author : Sjoerd.J.Schaper - vspickelen 'Date : 03-11-2006 'Code : all QBasic's, FreeBasic extendable 'Keys : [Esc] quit program ' [0]...[3] set thirds stack ' [F1]...[F7] choose scale degree, ' next press [0]...[3] for an inversion ' [Enter] switch to loop mode ' [Space] skip CA updates ' [Backspc] reverse the simulation ' [+] and [-] adjust wave amplitude ' [Tab] choose left/ right string, ' [Up] or [Down arrow] to change wave speed ' press any other key for random noise '****************************************************************************** 'This program is copyright (c) 2006 by the author. It is made available as is, 'and no warranty - about the program, its performance, or its conformity to any 'specification - is given or implied. It may be used, modified, and distributed 'freely, as long as the original author is credited as part of the final work. '****************************************************************************** ' DECLARE SUB populate (t AS INTEGER) 'display the automaton at time t DECLARE SUB evaluate () 'apply the linear wave rule DECLARE FUNCTION chords% (fl AS INTEGER) 'seed with diatonic chords 'note: replace qb suffix% with AS INTEGER to compile w/fb 0.17b DECLARE SUB FMod (f AS SINGLE, fm() AS SINGLE, r AS INTEGER) 'modulate sine wave carrier DECLARE SUB wryte (fl AS INTEGER) 'create .wav-soundfile CONST MODE = 12, dW = 640, dH = 480 ' set screenmode, width, height CONST BASF = 48 ' base velocity CONST SRAT = 16000 ' sample rate CONST MAX = SRAT * (BASF / 264) ' tuned stringlength CONST GAIN = 1.26, DWV = 1.07 ' parameter defaults DIM SHARED c(MAX, 3) AS SINGLE, Wav(1) AS SINGLE ' cell states, wave parameter DIM SHARED lr(1, 1) AS INTEGER, sw AS INTEGER ' generation switches DIM SHARED plot AS INTEGER, pile AS INTEGER ' gfx switch, pile o'thirds DIM SHARED deg AS INTEGER, inv AS INTEGER ' scale degree, inversion DIM auto AS INTEGER, dt AS INTEGER, fl AS INTEGER DIM k AS INTEGER, r AS INTEGER, sc AS INTEGER DIM t AS INTEGER, tmx AS INTEGER, ud AS INTEGER DIM a AS SINGLE, v AS SINGLE, g AS STRING RANDOMIZE TIMER auto = 0: fl = 0: ud = -1 dt = 1: t = 0 DO IF ud THEN populate t wryte -1: t = t + dt IF ud THEN evaluate IF t = 0 AND dt = -1 THEN LOCATE 3, 29: PRINT "key..." SLEEP LOCATE 3, 29: PRINT " " WHILE INKEY$ <> "": WEND sw = 1 - sw dt = 1: t = 1 END IF g = INKEY$ IF g <> "" THEN sc = ASC(RIGHT$(g, 1)) IF LEN(g) > 1 THEN sc = sc + 128 SELECT CASE sc CASE 8 sw = 1 - sw dt = -dt: t = t + dt ' reverse time CASE 9 fl = 1 - fl ' toggle l/ r CASE 13 auto = -1: tmx = 12 ' largo loop deg = 0: inv = 0 dt = 1: t = chords(0) CASE 27 EXIT DO ' quit CASE 32 ud = NOT ud ' freeze CASE 43, 45 plot = NOT plot: a = GAIN IF sc = 45 THEN a = 1 / a FOR k = 0 TO MAX - 1 ' gain FOR r = 0 TO 3 v = c(k, r) * a IF ABS(v) > 1 THEN v = SGN(v) c(k, r) = v NEXT r NEXT k CASE 48 TO 57 pile = (sc - 48) MOD 4 ' thirds CASE 187 TO 193 auto = 0 deg = sc - 187 ' degree WHILE INKEY$ <> "": WEND DO: g = INKEY$ LOOP WHILE g = "" inv = ASC(RIGHT$(g, 1)) - 48 ' inversion dt = 1: t = chords(0) CASE 200, 208 a = Wav(fl) * DWV ' speed IF sc = 208 THEN a = Wav(fl) / DWV IF a < 1 THEN Wav(fl) = a LOCATE 28, 2: PRINT "speed left:"; Wav(0); " "; LOCATE 28, 29: PRINT "right:"; Wav(1); " "; CASE ELSE auto = 0 deg = -1: inv = -1 dt = 1: t = chords(0) ' reset END SELECT WHILE INKEY$ <> "": WEND END IF IF auto AND (t > tmx) THEN dt = 1: t = chords(-1) END IF LOOP wryte 0 SYSTEM 'interval ratios DATA 1,1, 9,8, 6,5, 5,4, 4,3, 64,45 DATA 3,2, 5,3, 16,9, 9,5, 15,8 'chords table DATA "I","II","III","IV","V","VI","VII" 'primes DATA 0, 1, 3, 4, 6, 7, 10 'thirds DATA 3, 2, 2, 3, 3, 2, 2 'fifths DATA 6, 6, 6, 6, 6, 6, 5 'sevenths DATA 10, 8, 9, 10, 8, 8, 8 FUNCTION chords% (auto AS INTEGER) 'note: replace qb suffix% with AS INTEGER to compile w/fb 0.17b STATIC dep AS INTEGER, fl AS INTEGER STATIC dd() AS INTEGER, ix() AS INTEGER STATIC iv() AS SINGLE, sy() AS STRING DIM a AS SINGLE, ch(3) AS SINGLE, fm(2, 1) AS SINGLE DIM d(1) AS INTEGER, dr AS INTEGER, k AS INTEGER, r AS INTEGER DIM r2 AS INTEGER, s AS INTEGER, sg AS INTEGER, t AS INTEGER ' IF NOT fl THEN REDIM dd(3) AS INTEGER, ix(3, 6) AS INTEGER REDIM iv(10) AS SINGLE, sy(6) AS STRING ' FOR t = 0 TO 10 ' read intervals READ r, s iv(t) = r / s NEXT t FOR t = 0 TO 6 ' read symbols READ sy(t) NEXT t FOR r = 0 TO 3 ' read indices FOR t = 0 TO 6 READ ix(r, t) NEXT t NEXT r dd(0) = 3: dd(1) = 1 dd(2) = 5: dd(3) = 6 fl = -1 END IF 'FM parameters left/ right fm(0, 0) = .5: fm(0, 1) = 1.25 ' modulator 1 & 2 mult's fm(1, 0) = .5: fm(1, 1) = .75 fm(2, 0) = 6: fm(2, 1) = 4 ' deviation 1 & 2 d(0) = CINT(MAX * .618034) d(1) = CINT(MAX * .3183099) ' phase shift l/r LOCATE 2, 2: sg = -1 IF auto THEN DO t = INT(RND * 4) ' random progression r = (deg + dd(t)) MOD 7 LOOP WHILE r = dep dep = deg: deg = r inv = 0 IF t = 3 THEN inv = 1 + INT(RND * 3) END IF ELSE IF inv < 0 THEN FOR r = 0 TO 2 FOR t = 0 TO 1 ' random noise s = 3: IF r > 1 THEN s = 9 fm(r, t) = .125 + RND * s NEXT t NEXT r: sg = 0 inv = INT(RND * (pile + 1)) END IF IF deg < 0 THEN ' random degree deg = INT(RND * 7): sg = 0 END IF END IF inv = inv MOD (pile + 1) IF sg THEN PRINT sy(deg); inv; " "; ELSE PRINT "random"; END IF ch(0) = BASF * iv(ix(0, deg)) FOR t = 1 TO pile ' stack thirds ch(t) = ch(0) * iv(ix(t, deg)) NEXT t deg = (deg + inv * 2) MOD 7 IF inv > 0 THEN ' invert FOR t = 0 TO inv - 1 ' bass ch(inv) ch(t) = ch(t) * 2 NEXT t IF (ch(inv) - .01) > BASF * iv(10) THEN ' b FOR t = 0 TO pile ch(t) = ch(t) * .5 NEXT t END IF END IF FOR t = 1 TO pile r = (inv + t) MOD (pile + 1) IF (ch(r) + .01) < BASF * iv(8) THEN ' b-flat ch(r) = ch(r) * 2 END IF NEXT t LOCATE 2, 28: PRINT SPACE$(20); LOCATE 2, 28 FOR t = 0 TO pile PRINT CINT(ch(t) * SRAT / MAX); NEXT t dr = 1 FOR r = 0 TO 1 r2 = r + r FOR k = 0 TO MAX - 1 c(k, r2) = 0 NEXT k FOR t = 0 TO pile ' FM timbre FMod ch(t), fm(), r2 NEXT t a = 0 FOR k = 0 TO MAX - 1 ' shift c(k, dr) = c((k + d(r)) MOD MAX, r2) IF ABS(c(k, dr)) > a THEN a = ABS(c(k, dr)) NEXT k a = .615 * LOG(pile + 2) / a FOR k = 0 TO MAX - 1 c(k, dr) = c(k, dr) * a ' normalize c(k, r2) = c(k, dr) ' copy back NEXT k dr = 3 NEXT r chords = 0 END FUNCTION SUB evaluate DIM a AS SINGLE, u AS SINGLE, ul AS SINGLE, ur AS SINGLE DIM k AS INTEGER, s AS INTEGER, s1 AS INTEGER, t AS INTEGER ' FOR t = 0 TO 1 s1 = lr(t, 1 - sw) s = lr(t, sw) ul = c(MAX - 1, s) ' wrap boundary c(MAX, s) = c(0, s) FOR k = 0 TO MAX - 1 u = c(k, s) ur = c(k + 1, s) a = ul + ur: ul = u: u = u + u a = (a - u) * Wav(t) + u - c(k, s1) IF ABS(a) > 1 THEN a = SGN(a) ' clip c(k, s1) = a ' update CA NEXT k NEXT t sw = 1 - sw ' switch generations END SUB SUB FMod (f AS SINGLE, fm() AS SINGLE, r AS INTEGER) STATIC fl AS INTEGER, sine() AS SINGLE DIM i(1) AS SINGLE, v(2) AS LONG DIM a AS INTEGER, k AS INTEGER, w AS SINGLE ' IF NOT fl THEN REDIM sine(MAX - 1) AS SINGLE ' w = 8 * ATN(1) / MAX FOR k = 0 TO MAX - 1 ' base oscillator sine(k) = SIN(k * w) NEXT k fl = -1 END IF v(0) = f * fm(r \ 2, 0) ' modulator ratios, v(1) = f * fm(r \ 2, 1) v(2) = CLNG(f) i(0) = MAX * fm(2, 0) / v(0) ' indices i(1) = MAX * fm(2, 1) / v(1) FOR k = 0 TO MAX - 1 a = i(0) * sine(k * v(0) MOD MAX) ' two-into-one a = a + i(1) * sine(k * v(1) MOD MAX) c(k, r) = c(k, r) + sine((k * v(2) + a) MOD MAX) NEXT k END SUB SUB populate (gen AS INTEGER) STATIC fl AS INTEGER, pl AS INTEGER DIM i AS INTEGER, k AS INTEGER, r AS INTEGER DIM s AS INTEGER, s1 AS INTEGER, t AS INTEGER ' IF (gen = 0) OR (pl XOR plot) THEN IF NOT fl THEN SCREEN MODE COLOR 7: fl = -1 VIEW (2, 79)-(dW - 3, dH - 80), , 7 WINDOW (0, -2)-(MAX - 1, 2) sw = 0 ' initialize lr(0, sw) = 0: lr(0, 1 - sw) = 1 'left, lr(1, sw) = 2: lr(1, 1 - sw) = 3 'right string Wav(0) = .06: Wav(1) = .09 ' wave speed deg = -1: inv = -1 pile = 3: plot = chords(0) LOCATE 3, 2: PRINT "genr."; LOCATE 28, 2: PRINT "speed left:"; Wav(0); LOCATE 28, 29: PRINT "right:"; Wav(1); END IF pl = plot LOCATE 3, 8: PRINT " "; LINE (0, -2)-(MAX - 1, 2), 0, BF LINE (0, 0)-(MAX - 1, 0), 7 END IF LOCATE 3, 7: PRINT gen; " "; i = 1 FOR r = 0 TO 1 s1 = lr(r, 1 - sw) s = lr(r, sw) FOR k = 1 TO MAX - 1 ' plot curves l/r t = k - 1 LINE (t, c(t, s1) + i)-(k, c(k, s1) + i), 0 LINE (t, c(t, s) + i)-(k, c(k, s) + i), 7 NEXT k: i = -1 NEXT r END SUB SUB wryte (wr AS INTEGER) STATIC a AS INTEGER, fl AS INTEGER, i AS LONG DIM g AS STRING, k AS INTEGER, p AS INTEGER DIM sl AS INTEGER, sr AS INTEGER DIM word AS STRING * 2 DIM dword AS STRING * 4 ' IF NOT fl THEN OPEN ".\CAsounds.wav" FOR BINARY AS #1 'RIFF type chunk i = 1 dword = "RIFF" PUT #1, i, dword ' chunk ID dword = "WAVE" PUT #1, i + 8, dword ' RIFF type i = i + 12 'Format chunk dword = "fmt " PUT #1, i, dword ' chunk ID dword = MKL$(16) PUT #1, i + 4, dword ' chunk data size word = MKI$(1) PUT #1, i + 8, word ' compression code word = MKI$(2) PUT #1, i + 10, word ' number of channels dword = MKL$(SRAT) PUT #1, i + 12, dword ' sample rate p = SRAT * 2: dword = MKL$(p) PUT #1, i + 16, dword ' average bytes per second word = MKI$(2) PUT #1, i + 20, word ' block align word = MKI$(8) PUT #1, i + 22, word ' significant bits per sample i = i + 24 'Data chunk dword = "data" PUT #1, i, dword ' chunk ID i = i + 8: fl = -1 a = 126 ' -.1 dB peak END IF IF wr THEN sl = lr(0, sw) sr = lr(1, sw): p = 1 g = STRING$(MAX * 2, " ") FOR k = 0 TO MAX - 1 MID$(g, p, 1) = CHR$(CINT(c(k, sl) * a) + 128) MID$(g, p + 1, 1) = CHR$(CINT(c(k, sr) * a) + 128) p = p + 2 NEXT k PUT #1, i, g i = i + MAX * 2 ELSE dword = MKL$(i - 8) PUT #1, 5, dword ' filelength - 8 dword = MKL$(i - 44) PUT #1, 41, dword ' data chunk size CLOSE END IF END SUB