1// SYSTEM ONE — drum & bass, 174bpm, 64 bars as written; Jev arranges up to 24 4-bar sections. Spec: SPEC.md 2// Start from STOPPED so the song begins at bar 1 (re-running it mid-play keeps the cycle count). 3// Half-time "thinking" is made with patterns; cps never changes, because every mask counts cycles. 4setcps(174/60/4) 5await samples('github:tidalcycles/dirt-samples') 6await samples('jev-samples/strudel.json') 7 8// ── Jev: two of them, in TypeSafe's own shape (SPEC.md, "Jev in the booth") ── 9// formJev picks the next section; djJev, asked once that is decided (questions 10// in one request cannot see each other), says how much of each part plays 11// it, how the song moves into it, and how hard. Every 4 bars. The form's rules (walk, at the bottom) narrow the 12// sections Jev may pick and end the song; without Jev it plays as written. 13const song = { 14 title: 'SYSTEM ONE', 15 what: 'drum and bass about Jev as System One, in Kahneman terms: the beat collapses to half time while an LLM thinks, and snaps back when Jev answers', 16 tempo: '174 bpm; the think sections feel half time at the same tempo; 4 bars a section', 17 key: 'E minor', 18 chords: 'Em C G D, one bar each; every section starts on Em', 19 lyrics: { 20 intro: 'Thinking, fast and slow', 21 intro2: 'Thinking, fast and slow', 22 think1: 'Let him cook', 23 drop1: 'Noul. Choice. Score.', 24 think2: 'Let him cook now', 25 drop2: 'Noul. Choice. Score.', 26 think3: 'Let him cook', 27 drop3: 'Noul. Choice. Score.', 28 outro: 'Noul. Choice. Score.', 29 }, 30} 31 32const formJev = jev({ 33 state: song, 34 questions: { 35 section: choice('Pick the section of the song that plays next, from those allowed. The outro ends the song: choose it once the song has built to its peak and made its point, before it drags', { 36 intro: 'the amen break through a falling high-pass filter and a sub pulse; a robot voice', 37 intro2: 'the filtered intro continues; the robot voice again', 38 think1: 'half time, the build: the LLM is thinking; kick on 1, snare on 3, the reese held low, a Windows chime phrase that resolves to E', 39 drop1: 'drop 1 lands: full speed two-step and rolling amen, eighth hats, the reese bass; Jev answers', 40 drop1b: 'drop 1 continues, instrumental', 41 drop1c: 'drop 1 continues, instrumental', 42 drop1d: 'the end of drop 1, instrumental', 43 think2: 'half time, the build: a busier Windows chime phrase that resolves to E', 44 drop2: 'drop 2 lands: drop 1 plus sixteenth hats and chord stabs; Jev answers', 45 drop2b: 'drop 2 continues, instrumental', 46 drop2c: 'drop 2 continues, instrumental', 47 drop2d: 'the end of drop 2, instrumental', 48 think3: 'half time, the build: the chime stops on F#, never resolves', 49 drop3: 'drop 3 lands, the densest drop: stabs, sixteenth hats and the reese doubled an octave up; Jev answers', 50 drop3b: 'drop 3 continues, the peak, instrumental', 51 outro: 'the hook once more, then a final kick, crash and Em chord: the end', 52 }), 53 }, 54}) 55 56// how much of each part the section decidedThisSection has: out, back or full 57const level = (part) => score('How much of ' + part + ' should the next section (decidedThisSection) have? Drop parts to build tension or make space, pull them back to let others speak, and bring them in full for the payoff; keep what defines the section', [ 58 'out: silent for the section', 59 'back: under the rest of the mix', 60 'full: as written', 61]) 62const djJev = jev({ 63 state: song, 64 questions: { 65 drums: level('the drums (the amen break filtered in the intro; full-speed two-step kick and rolling amen in every drop; in the thinks, kick on 1, snare on 3, quarter hats, no amen)'), 66 bass: level('the bass (a sub pulse in the intro, the reese and sub on the chord roots in every drop, doubled an octave up in drop 3, the reese held low on E in every think: the floor under the whole song)'), 67 stabs: level('the chord stabs (short saw chords in drops 2 and 3 only, what makes them bigger than drop 1)'), 68 chime: level('the loading chime (the Windows chimes sound played as a phrase in each think only, the sound of the LLM thinking: think1 and think2 resolve to E, think3 stops on F#)'), 69 vocals: level('the vocals (the lines in the lyrics: the robot in the intro, let him cook in the thinks, the hook as each drop lands, and once more to start the outro)'), 70 into: choice('How should the song move into the next section (decidedThisSection)? It plays over the last bar of the section before it', { 71 cut: 'straight in, no transition', 72 fill: 'the amen break stuttering on its snare across the second half of the bar', 73 riser: 'white noise swelling up over the last bar', 74 dropout: 'everything stops for the last beat, a gap before the section hits', 75 }), 76 energy: score('How intense should the next section be? Shape the energy of the whole song, following its history, how it measured and how the listener reacted', [ 77 'held back: about half volume, room to breathe', 78 'steady: well under full', 79 'driving: nearly full', 80 'all out: full volume, nothing held back', 81 ]), 82 }, 83}) 84 85// a little chance in the form: the section is drawn from Jev's probabilities 86const { section } = formJev.every(4, { sample: 0.7 }) 87const dj = djJev.every(4, { 88 after: { section }, 89 forget: ['into'], 90 fallback: { drums: 2, bass: 2, stabs: 2, chime: 2, vocals: 2, into: 'cut', energy: 3 }, 91 // the opening has no section before it, so nothing to move from: it cuts 92 allow: { into: (d) => (d.playingNow ? ['cut', 'fill', 'riser', 'dropout'] : ['cut']) }, 93}) 94 95// A part at Jev's level, in whole steps (a score of 1.5 plays as full, not 96// between): out is silent, back is half velocity, full is as written. Each 97// part has its own orbit, named for its question, so the meter hears it 98// alone (measuredSound). 99const lvl = (name) => dj[name].score.round() 100const part = (name, pat) => 101 pat.orbit(name).mask(lvl(name).gt(0)).mul(velocity(lvl(name).div(2))) 102 103// bars: 1-8 intro | 9-12 think | 13-28 DROP 1 | 29-32 think | 33-48 DROP 2 104// 49-52 think | 53-60 DROP 3 | 61-64 outro (each mask = 64 steps) 105const intro = "<1!8 0!56>" 106const thinks = "<0!8 1!4 0!16 1!4 0!16 1!4 0!12>" 107const think1 = "<0!8 1!4 0!52>" 108const think2 = "<0!28 1!4 0!32>" 109const think3 = "<0!48 1!4 0!12>" 110const drops = "<0!12 1!16 0!4 1!16 0!4 1!8 0!4>" 111const drop1 = "<0!12 1!16 0!36>" 112const bigger = "<0!32 1!16 0!4 1!8 0!4>" // drops 2 and 3 113const drop3 = "<0!52 1!8 0!4>" 114const hook = "<0!12 1 0!19 1 0!19 1 0!7 1 0!3>" // bars 13, 33, 53, 61 115const crash = "<0!12 1 0!19 1 0!19 1 0!11>" 116const robot = "<0!2 1 0!3 1 0!57>" 117const cook = "<0!8 1 0!39 1 0!15>" 118const cookNow = "<0!28 1 0!35>" 119const rise = "<0!61 1 0!2>" // bar 62, into the final hit 120const lastHit = "<0!62 1 0>" 121 122// one bar per chord: Em C G D. No root under 41 Hz: C and D sit above E. 123const roots = "<28 36 31 38>" // E1 C2 G1 D2, as MIDI numbers 124const chords = "<[e4,g4,b4] [c4,e4,g4] [d4,g4,b4] [d4,f#4,a4]>" 125const amen = "<[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15] [16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31] [0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15] [0 1 2 3 4 5 6 7 [12 12] [12 12] 14 14 [30 30] [30 30] 31 31]>" 126 127// ── parts ───────────────────────────────────────── 128// drums: the filtered intro break, full speed (System One), and half time 129// (the LLM is thinking). A short kick, no 808 tail: the sub carries the 130// pitch. The break is choked (cut), so no slice outlasts its 16th. 131const drums = stack( 132 n("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15").s("amencutup").cut(1) 133 .hpf(saw.range(3000, 400).slow(8)).gain(.8).mask(intro), 134 s("bd:3").struct("x ~ ~ ~ ~ ~ ~ ~ ~ ~ x ~ ~ ~ ~ ~").gain(.7).mask(drops), 135 s("sn:3").struct("~ ~ ~ ~ x ~ ~ ~ ~ ~ ~ ~ x ~ ~ ~").room(.15).gain(1).mask(drops), 136 n(amen).s("amencutup").cut(1).hpf(150).gain(.72).mask(drops), 137 s("hh*8").gain("1.75 .9").mask(drop1), 138 s("hh*16").gain("1.75 .75 1.25 .75").mask(bigger), 139 s("hh27:1").gain(2.5).mask(crash), // hh27:1 is the crash; `cr` is a ride 140 s("bd:3").struct("x ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~").gain(.45).mask(thinks), 141 s("sn:3").struct("~ ~ ~ ~ ~ ~ ~ ~ x ~ ~ ~ ~ ~ ~ ~").room(.4).gain(.52).mask(thinks), 142 s("hh*4").gain(.2).mask(thinks) 143) 144 145// bass: the intro sub, the reese in the drops (doubled an octave up in drop 146// 3), and the reese held on E, filtered down, while thinking. The think 147// notes stop after beat 3 of the section's last bar, so a dropout is silent. 148const bassRhythm = "x@3 x@3 x@4 x@2 x@4" 149const thinkE = "<28 28 28 [28@3 ~]>" 150const bass = stack( 151 note("28").s("sine").decay(.6).sustain(0).gain(.5).mask(intro), 152 note(roots).struct(bassRhythm).s("supersaw").detune(.6) 153 .lpf(sine.range(220, 1000).slow(8)).lpq(3).shape(.3).gain(.45).mask(drops), 154 note(roots).struct(bassRhythm).add(note(12)).s("supersaw").detune(.6) 155 .lpf(sine.range(600, 2400).slow(8)).lpq(2).gain(1.8).mask(drop3), 156 note(roots).struct(bassRhythm).s("sine").gain(.95).mask(drops), 157 note(thinkE).s("supersaw").detune(.6).lpf(280).attack(.05).release(.1).gain(.2).mask(thinks), 158 note(thinkE).s("sine").release(.05).gain(.27).mask(thinks) 159) 160 161// the chord stabs in drops 2 and 3 162const stabs = note(chords).struct("x ~ ~ x ~ ~ ~ ~ ~ ~ x ~ ~ ~ ~ ~") 163 .s("sawtooth").lpf(3000).decay(.15).sustain(0).room(.3).gain(2.3).mask(bigger) 164 165// the loading chime while thinking: the Windows chimes sound (samples/ 166// sowinchime, rooted on A5 +8 cents) played as notes, so each think ends 167// its own way: think1 and think2 resolve to E, think3 stops on F#. It 168// starts after "Let him cook", and its last bar is short, silent by beat 4. 169const winChime = (phrase, decay) => 170 note(phrase).transpose(-45).speed(.995).s("sowinchime").decay(decay).sustain(0) 171const chime = stack( 172 winChime("<[~ ~ ~ [~ b5]] [e5 ~ g5 ~] [f#5 ~ d5 ~] [e5 ~ ~ ~]>", "<1.2 1.2 1.2 .9>") 173 .room(.5).size(.8).gain(.5).mask(think1), 174 winChime("<[~ ~ ~ [~ b5]] [e5 g5 b5 e5] [a5 d5 f#5 ~] [g5 e5 ~ ~]>", "<1.4 1.4 1.4 .5>") 175 .room(.5).size(.8).gain(.42).mask(think2), 176 winChime("<[~ ~ ~ [~ b5]] [e5 ~ g5 ~] [f#5 ~ d5 ~] [f#5 ~ ~ ~]>", "<2 2 2 .9>") 177 .room(.5).size(.8).gain(.5).mask(think3) 178) 179 180const vox = stack( 181 s("sothinking").gain(.9).room(.5).delay(.3).delaytime(.345).delayfeedback(.35).mask(robot), 182 s("socook").gain(1.3).room(.3).mask(cook), 183 s("socooknow").gain(1.3).room(.3).mask(cookNow), 184 // Jev answers: the full-speed beat snaps back under it 185 s("sohook").gain(2.2).room(.2).mask(hook) 186) 187 188// the end: a riser over bar 62 into a kick, crash and an Em chord left to 189// ring in bar 63. It always plays, on its own orbit. 190const ending = stack( 191 s("white*16").hpf(saw.range(400, 8000)).gain(saw.range(.2, 1.2)).decay(.07).sustain(0).mask(rise), 192 s("bd:3").room(.6).size(.9).gain(.6).mask(lastHit), 193 s("hh27:1").room(.6).size(.9).gain(2.5).mask(lastHit), 194 note("[e3,g3,b3]").s("sawtooth").lpf(1400).decay(1.5).sustain(0).room(.6).gain(.2).mask(lastHit) 195).orbit('ending') 196 197const once = (p) => p.mask("<1!64 0!936>") // play once, then silence 198 199// ── the form, as rules in code: which sections may follow which ── 200// `at` is where each starts as written; an empty `next` ends the song. The 201// written order is always allowed, and is what plays without Jev. 202// Sections are the song's 4-bar grid: every drop and think starts on one, 203// on Em, and each think leads only into its own drop. The cap is 24 204// sections (96 bars, ~2:12), since 16 would only be the written length. 205const form = walk(section, { 206 max: 24, 207 sections: { 208 intro: { at: 0, next: ['intro2'] }, 209 intro2: { at: 4, next: ['think1'] }, 210 think1: { at: 8, next: ['drop1'] }, 211 drop1: { at: 12, next: ['drop1b'] }, 212 drop1b: { at: 16, next: ['drop1c', 'think2'] }, 213 drop1c: { at: 20, next: ['drop1d'] }, 214 drop1d: { at: 24, next: ['think2', 'think3'] }, 215 think2: { at: 28, next: ['drop2'] }, 216 drop2: { at: 32, next: ['drop2b'] }, 217 drop2b: { at: 36, next: ['drop2c', 'think3'] }, 218 drop2c: { at: 40, next: ['drop2d'] }, 219 drop2d: { at: 44, next: ['think3', 'think2'] }, 220 think3: { at: 48, next: ['drop3'] }, 221 drop3: { at: 52, next: ['drop3b'] }, 222 drop3b: { at: 56, next: ['outro', 'think2', 'drop2'] }, 223 outro: { at: 60, next: [] }, 224 }, 225}) 226 227// ── the song: Jev's section, parts and energy, composed ── 228// Each section plays its own bars, each part at the level Jev chose; the 229// energy scales every part's velocity together, in whole steps, so the mix 230// keeps its balance. The ending always plays. 231const parts = form.arrange(once(stack( 232 part('drums', drums), 233 part('bass', bass), 234 part('stabs', stabs), 235 part('chime', chime), 236 part('vocals', vox), 237))) 238 239// ── into the next section: over the last bar of each, the next one's answer ── 240// Past the last section there is no next one, so nothing plays there. The 241// fill follows the drums level of the section whose bar it plays in (no 242// read-ahead: that is the answer it sounds in); the riser has its own orbit. 243const into = dj.into.choice.early(1) 244const transition = into.pick({ 245 cut: silence, 246 fill: n("~ ~ ~ ~ ~ ~ ~ ~ [12 12] [12 12] [12 12 12] [12 12 12] [30 30] [30 30 30] [30 30 30 30] 31") 247 .s("amencutup").cut(1).hpf(150).gain(saw.range(.5, .8)).orbit('drums') 248 .mask(lvl('drums').gt(0)).mul(velocity(lvl('drums').div(2))), 249 riser: s("white*16").hpf(saw.range(400, 8000)).gain(saw.range(.05, .35)).decay(.07).sustain(0).orbit('into'), 250 dropout: silence, 251}).mask("<0!3 1>") 252// dropout: the parts stop for the section's last beat. The outro has no 253// next section, so its last beat plays. 254const lastBeat = "<0!3 [0 0 0 1]>" 255const gap = stack( 256 "<1!3 [1 1 1 0]>", 257 into.fmap((c) => c !== 'dropout').mask(lastBeat), 258 section.choice.fmap((s) => s === 'outro').mask(lastBeat), 259) 260 261// the master: the mix peaks at or under -1 dBFS (measured, tools/measure) 262stack(parts.mask(gap), transition, form.arrange(once(ending))) 263 .mul(velocity(dj.energy.score.round().div(3).range(.55, 1))) 264 .mul(postgain(.24)) 265