From 9fe26bb6790a6dc3267a463e561abb1d21562dc4 Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Sun, 4 Oct 2026 18:33:54 -0700 Subject: [PATCH] =?UTF-8?q?sailor-song=20v119:=20the=20critics'=20three=20?= =?UTF-8?q?=E2=80=94=2052=20Hz=20lane=20down=20~3=20dB=20(substrate=20+2.2?= =?UTF-8?q?=20=E2=86=92=20+0.2,=20master=20low=20shelf=20=E2=88=923.5),=20?= =?UTF-8?q?a=2020=20ms=20fade=20at=20her=20stems'=20end=20so=20the=20camer?= =?UTF-8?q?a=20handling=20doesn't=20hard-cut,=20the=20break=20pings=20unde?= =?UTF-8?q?r=20the=20second=20long=20tucked=20=C3=970.9;=20video=20agent's?= =?UTF-8?q?=20cover.py=20/=20tag.py=20/=20relight=20edits?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pop/sailor-song/bin/cover.py | 18 ++++++++----- pop/sailor-song/bin/perf-relight.mjs | 24 +++++++++++------- pop/sailor-song/bin/tag.py | 38 ++++++++++++++++++++++++++++ pop/sailor-song/c/cut.sh | 3 ++- pop/sailor-song/c/sailorremix.c | 9 ++++--- 5 files changed, 72 insertions(+), 20 deletions(-) create mode 100644 pop/sailor-song/bin/tag.py diff --git a/pop/sailor-song/bin/cover.py b/pop/sailor-song/bin/cover.py index 611877882e..fb37a4bc3a 100644 --- a/pop/sailor-song/bin/cover.py +++ b/pop/sailor-song/bin/cover.py @@ -9,8 +9,8 @@ violet through feathered hue masks, hard S, blacks down), upscaled locally (lanczos + mild unsharp; nothing leaves this machine) and grained to hide the upscale. - pop/.venv/bin/python pop/sailor-song/bin/cover.py [--t 11.583] [--x 110] - → cover/sailor-song-cover.jpg (clean) + cover/sailor-song-cover-title.jpg + pop/.venv/bin/python pop/sailor-song/bin/cover.py [--t 11.583] [--x 110] [--src src/take.mov] [--out cover/stem] + → .jpg (clean) + -title.jpg; default stem cover/sailor-song-cover """ import argparse, os, subprocess import numpy as np @@ -22,12 +22,16 @@ ap.add_argument("--t", type=float, default=107.15) # v6.2: eyes open at the le ap.add_argument("--x", type=int, default=80) # square crop left edge (of 960) ap.add_argument("--size", type=int, default=3000) ap.add_argument("--debug", default="") # v8: dump the grade masks here +ap.add_argument("--src", default="") # the take; default src/take.mov (the Desktop IMG_8699.mov copy is gone) +ap.add_argument("--out", default="") # output stem; default cover/sailor-song-cover a = ap.parse_args() LANE = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) OUT = os.path.join(LANE, "cover"); os.makedirs(OUT, exist_ok=True) +SRC = a.src or os.path.join(LANE, "src", "take.mov") +STEM = a.out or os.path.join(OUT, "sailor-song-cover"); os.makedirs(os.path.dirname(STEM), exist_ok=True) W, H = 960, 540 -raw = subprocess.run(["ffmpeg", "-v", "error", "-ss", str(a.t), "-i", os.path.expanduser("~/Desktop/IMG_8699.mov"), +raw = subprocess.run(["ffmpeg", "-v", "error", "-ss", str(a.t), "-i", SRC, "-vf", "scale=in_color_matrix=bt2020:in_range=tv:out_range=pc,format=rgb48le", "-frames:v", "1", "-f", "rawvideo", "-"], capture_output=True, check=True).stdout im = np.frombuffer(raw, dtype=" { const w = g.w, h = // QUANTIZED (jeffrey): one hard step per grain of the ki-ki-ki — the receipt's stutter events — held between, the // last step on the downbeat, then an ease back; chorus 2 gets a smaller bump keyed to its downbeat alone const STUT = (R.events || []).filter((e) => e.voice === "stutter").map((e) => e.t - T0).sort((a, b) => a - b); -const BUMPS = [{ t: 38.444, steps: STUT.filter((t) => t > 37.5 && t < 38.444), amt: 0.09 }, { t: 84.898, steps: [84.898 - 0.12, 84.898 - 0.06], amt: 0.05 }]; +const BUMPS = [{ t: 38.444, steps: STUT.filter((t) => t > 38.0 && t < 38.444), amt: 0.09 }, { t: 84.898, steps: [84.898 - 0.12, 84.898 - 0.06], amt: 0.05 }]; // the picture jumps only on the grains inside the stop (jeffrey: the early ones "unnecessary") // …and not a staircase: each grain JUMPS the frame somewhere — a hashed zoom (in or out) and a hashed offset — held until // the next grain, the downbeat landing the big push, then the ease back (jeffrey: "jumping around based on the glitch") const jumpOf = (t, amt) => { const h = fnv(`jump:${t.toFixed(3)}`); return { z: 1 + amt * (0.35 + 0.65 * ((h >>> 4) % 100) / 100) * (((h >>> 12) & 1) ? 1 : -0.45), dx: (((h >>> 16) % 100) / 100 - 0.5) * amt * 1.6, dy: (((h >>> 24) % 100) / 100 - 0.5) * amt * 1.0 }; }; @@ -345,13 +345,18 @@ function glide(now) { const E = energyAt(now); amb += (E - amb) * Math.min(1, (1 for (let i = 0; i < 3; i++) { tint[i] += (want[i] - tint[i]) * Math.min(1, (1 / FPS) / 0.7); hue[i] += (wantHue[i] - hue[i]) * Math.min(1, (1 / FPS) / 0.7); } return c; } const CAM_MOVE = 178.24; // take seconds: she picks the camera up; the room's lights are nowhere after that function drawFrame(fi) { - const now = T0 + FROM + fi / FPS, take = takeOf(now); matteAt(take); repairMatte(take); softenMatte(); if (STAGE) { depthAt(take); findFace(); } const c = glide(now); + const now = T0 + FROM + fi / FPS, take = takeOf(now); matteAt(take); repairMatte(take); softenMatte(); if (STAGE) depthAt(take); findFace(); const c = glide(now); // the relight arrives with the arrangement (v103: "the opening should look naturally lit too"): nothing through the intro, // rising from the kick's entrance (10.8 s) to full at chorus 1 (38.4 s); and a 2 s dissolve back to the plain picture // before she reaches for the camera const arrive = Math.min(1, Math.max(0, (now - T0 - 10.8) / (38.4 - 10.8))), arriveS = arrive * arrive * (3 - 2 * arrive); + // the dance (v103: "start formal, black and white, less shaky; as the ornament comes in, increase the shake and the + // colour, so the captions surprise the way the arrangement does"): 0 through the intro and verse 1, a first step + // when the kick lands, half at chorus 1, full from chorus 2, resting in the break, back up through the bridge + const DANCE = [[-99, 0], [2.1, 0], [10.8, 0.15], [23.6, 0.2], [38.4, 0.55], [69.1, 0.35], [84.9, 1], [115.3, 0.3], [124.8, 0.7], [139.9, 1]]; + const rn = now - T0; let dance = 0; for (let i = 1; i < DANCE.length; i++) { const [t0, d0] = DANCE[i - 1], [t1, d1] = DANCE[i]; if (rn >= t0 && rn < t1) { dance = d0 + (d1 - d0) * Math.min(1, (rn - t0) / 2.0); break; } if (rn >= t1) dance = d1; } const lightsOn = arriveS * (1 - Math.min(1, Math.max(0, (take - (CAM_MOVE - 2.3)) / 2.0))); - const toBlack = Math.min(1, Math.max(0, (take - (CAM_MOVE - 1.1)) / 0.9)); // v103 "align the ending": black by the time she reaches, the music's tail under it + const toBlack = 0; // no dip to black: jeffrey likes her moving the camera; the effects just dissolve off first const room = 1 + (0.72 + 0.28 * amb - 1) * lightsOn; // v103: a simple grade — the room breathes with the sections, never goes out; gone by the end const lampBloom = Math.min(0.5, env(KICKS, now, 0.08, 0.03)) * lightsOn, lampOn = (0.12 * amb + 0.2 * lampBloom) * lightsOn; // a burst, not a whiteout const bellRaw = env(BELLS, now, 1.4, 0.1), windowGlow = Math.min(0.15, 0.08 * bellRaw) * lightsOn, windowOn = (0.15 + 0.1 * amb) * lightsOn; @@ -377,7 +382,13 @@ function drawFrame(fi) { const gr = (g * T[0] + lampBloom * lamp * 0.1) * (1 - m) + fg * fgTint[0] * m, gg = (g * T[1] + lampBloom * lamp * 0.08) * (1 - m) + fg * fgTint[1] * m, gb = (g * T[2] + lampBloom * lamp * 0.06) * (1 - m) + fg * fgTint[2] * m; fb[p] = Math.min(255, fb[p] * gr); fb[p + 1] = Math.min(255, fb[p + 1] * gg); fb[p + 2] = Math.min(255, fb[p + 2] * gb); } // 2. the lamp's bloom and the window's glow, added to the room - if (longOn > 0) { const L = LIGHTS.lamp, lx = (L.x - VX) * sx, ly = (L.y - VY) * sy, c = roomHue(6), blink = roomPulse; glow(lx, ly, Math.round(L.r * 1.5 * sx), c[0], c[1], c[2], 0.12 * longOn * blink); } + // the ceiling flicker: a radial halo on the wall and ceiling BEHIND HER HEAD (jeffrey: "radial, behind her head, so it's + // more related"), its colour the beat's, pulsing with it; the room only (the matte keeps it off her) + const kickHalo = Math.min(1, env(KICKS, now, 0.12, 0.02)) * (0.04 + 0.26 * dance) * lightsOn; // the kick pumps it: a whisper at the start, hard by the choruses (jeffrey) + if (longOn > 0 || kickHalo > 0.01) { const c = longOn > 0 ? roomHue(6) : hue.map((h) => h * 0.9 + 25), k = Math.max(0.22 * longOn * roomPulse, kickHalo), R0 = (0.26 + 0.08 * kickHalo) * W, R2 = R0 * R0, hx = faceX, hy = faceY - 0.06 * H; + const y0 = Math.max(0, hy - R0) | 0, y1 = Math.min(H - 1, hy + R0) | 0, x0 = Math.max(0, hx - R0) | 0, x1 = Math.min(W - 1, hx + R0) | 0; + for (let y = y0; y <= y1; y++) for (let x = x0; x <= x1; x++) { const d2 = (x - hx) ** 2 + (y - hy) ** 2; if (d2 > R2) continue; const o = y * W + x, a = k * (1 - d2 / R2) ** 2 * (1 - M[o]), p = o * 3; + fb[p] = Math.min(255, fb[p] + c[0] * a); fb[p + 1] = Math.min(255, fb[p + 1] + c[1] * a); fb[p + 2] = Math.min(255, fb[p + 2] + c[2] * a); } } if (lampBloom > 0.02) { const L = LIGHTS.lamp, lx = (L.x - VX) * sx, ly = (L.y - VY) * sy, R0 = Math.round(L.r * 1.6 * sx); glow(lx, ly, R0, 255, 228, 180, 0.18 * lampBloom); glow(lx, ly, Math.round(R0 * 2.4), 255, 220, 170, 0.04 * lampBloom); } if (windowGlow > 0.02) { const Wn = LIGHTS.window; for (let y = Math.max(0, (Wn.y0 - VY) * sy); y < Math.min(H, (Wn.y1 - VY) * sy); y += 2) for (let x = Math.max(0, (Wn.x0 - VX) * sx); x < Math.min(W, (Wn.x1 - VX) * sx); x += 2) { const k = WINDOW[(y | 0) * W + (x | 0)] * 0.12 * windowGlow; add(x, y, 225, 238, 255, k); add(x + 1, y, 225, 238, 255, k); add(x, y + 1, 225, 238, 255, k); add(x + 1, y + 1, 225, 238, 255, k); } } // 3. the fairy lights: a bead of light each, glowing with the pads, a handful flashing on each hat @@ -390,11 +401,6 @@ function drawFrame(fi) { // 4. the singalong: the chunk being sung (and the one leaving); letters fill in syllable by syllable, the sung // syllable pops, the sung word a touch larger, a held syllable kerns outward and sways; the ball rides its one path. ZOOM = zoomAt(now - T0); upscale(); sharpen(); // the room is done at the base size, upscaled, SHARPENED, then the words go on - // the dance (v103: "start formal, black and white, less shaky; as the ornament comes in, increase the shake and the - // colour, so the captions surprise the way the arrangement does"): 0 through the intro and verse 1, a first step - // when the kick lands, half at chorus 1, full from chorus 2, resting in the break, back up through the bridge - const DANCE = [[-99, 0], [2.1, 0], [10.8, 0.15], [23.6, 0.2], [38.4, 0.55], [69.1, 0.35], [84.9, 1], [115.3, 0.3], [124.8, 0.7], [139.9, 1]]; - const rn = now - T0; let dance = 0; for (let i = 1; i < DANCE.length; i++) { const [t0, d0] = DANCE[i - 1], [t1, d1] = DANCE[i]; if (rn >= t0 && rn < t1) { dance = d0 + (d1 - d0) * Math.min(1, (rn - t0) / 2.0); break; } if (rn >= t1) dance = d1; } if (!NO_LYRIC) { const k = Math.min(1, 0.3 + 0.7 * amb) * (1 + 0.5 * lampBloom), fl = Math.min(1, bellRaw) * 120, SH = hue.map((h) => Math.min(255, (h * k + fl) * dance)), off = 0.07 * GA.px * (CHUNKS[0]?.sc ?? SC_MAX); // the shadow tight under the glyph (v103: "too far from the captions") // the "longs" (v103: "when arpeggiating should blink colors rapidly — psychic effects"): a held syllable's letters run // the hue wheel, each letter a step behind the last, blinking at 6 Hz, 16 Hz with the arp under it diff --git a/pop/sailor-song/bin/tag.py b/pop/sailor-song/bin/tag.py new file mode 100644 index 0000000000..2bfaf82b61 --- /dev/null +++ b/pop/sailor-song/bin/tag.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""tag.py — write the single's credits into an mp3 (ID3v2.4) and stamp the cover. + pop/.venv/bin/python pop/sailor-song/bin/tag.py --cover cover/x.jpg [--version v118] +Credits: Sage sings and plays (performer); Gigi Perez wrote the song (composer + lyricist); +Aesthetic Dot Computer produced, engineered, mixed and arranged the remix. +""" +import argparse, shutil +from mutagen.id3 import ID3, ID3NoHeaderError, TIT2, TPE1, TPE2, TALB, TCOM, TEXT, TIPL, TMCL, TCON, TDRC, COMM, APIC, TPUB, TIT3 +ap = argparse.ArgumentParser() +ap.add_argument("src"); ap.add_argument("dst") +ap.add_argument("--cover", required=True) +ap.add_argument("--version", default="") +ap.add_argument("--performer", default="Sage") +a = ap.parse_args() +shutil.copyfile(a.src, a.dst) +try: tags = ID3(a.dst) +except ID3NoHeaderError: tags = ID3() +tags.delete(a.dst); tags = ID3() +P, W, S = a.performer, "Gigi Perez", "Aesthetic Dot Computer" +tags.add(TIT2(encoding=3, text="Sailor Song")) +tags.add(TIT3(encoding=3, text="cover of Gigi Perez — remixed by Aesthetic Dot Computer")) +tags.add(TPE1(encoding=3, text=P)) # artist / performer +tags.add(TPE2(encoding=3, text=P)) # album artist +tags.add(TALB(encoding=3, text="Sailor Song")) +tags.add(TCOM(encoding=3, text=W)) # composer +tags.add(TEXT(encoding=3, text=W)) # lyricist +tags.add(TIPL(encoding=3, people=[["producer", S], ["engineer", S], ["mix", S], ["arranger", S]])) +tags.add(TMCL(encoding=3, people=[["vocals", P], ["nylon-string guitar", P]])) +tags.add(TPUB(encoding=3, text=S)) +tags.add(TCON(encoding=3, text="Pop")) +tags.add(TDRC(encoding=3, text="2026")) +note = f"{P} sings and plays Gigi Perez's 'Sailor Song' (writer: Gigi Perez). Produced, engineered, mixed and arranged by Aesthetic Dot Computer" + (f" ({a.version})" if a.version else "") + "." +tags.add(COMM(encoding=3, lang="eng", desc="", text=note)) +with open(a.cover, "rb") as f: tags.add(APIC(encoding=3, mime="image/jpeg", type=3, desc="Cover", data=f.read())) +tags.save(a.dst, v2_version=4) +print("✓", a.dst) +for k, v in ID3(a.dst).items(): + print(" ", k, (f"<{len(v.data)} bytes {v.mime}>" if k.startswith("APIC") else (v.people if hasattr(v, "people") else str(v)))) diff --git a/pop/sailor-song/c/cut.sh b/pop/sailor-song/c/cut.sh index 69d731e053..43fb28ae5f 100755 --- a/pop/sailor-song/c/cut.sh +++ b/pop/sailor-song/c/cut.sh @@ -25,8 +25,9 @@ echo "→ translation premaster" if [ "${SUBSTRATE:-vinyl}" = "vinyl" ]; then CHAIN="volume=-4dB,$(node -e 'import("'"$LANE"'/../lib/substrate.mjs").then(m=>process.stdout.write(m.masterChain("vinyl")))')" # v60b: −4 dB in, so the glue stops pinning # v101 (measured): the chain's 4.4 Hz "flutter" was a ±26-cent chorus pedal on her voice → ±2 cents; its second compressor + # v119: the substrate's +2.2 dB at 52 Hz → +0.2 — the record's 52 Hz lane was 37–43 % of all energy in the choruses # (3.2:1, 10 ms, +2.4 makeup) flattened the arc to a 2.4 LU window and shaved the kick's first 15 ms → 1.5:1, 30 ms, no makeup - CHAIN="$(printf '%s' "$CHAIN" | sed -e 's/vibrato=f=4.4:d=0.22/vibrato=f=4.4:d=0.02/' -e 's/acompressor=threshold=-12dB:ratio=3.2:attack=10:release=160:makeup=2.4:knee=8/acompressor=threshold=-12dB:ratio=1.5:attack=30:release=160:makeup=1:knee=8/')" + CHAIN="$(printf '%s' "$CHAIN" | sed -e 's/equalizer=f=52:t=q:w=0.9:g=2.2/equalizer=f=52:t=q:w=0.9:g=0.2/' -e 's/vibrato=f=4.4:d=0.22/vibrato=f=4.4:d=0.02/' -e 's/acompressor=threshold=-12dB:ratio=3.2:attack=10:release=160:makeup=2.4:knee=8/acompressor=threshold=-12dB:ratio=1.5:attack=30:release=160:makeup=1:knee=8/')" echo " substrate: vinyl" else CHAIN="highpass=f=32,bass=g=-2.5:f=90:w=0.7,equalizer=f=220:t=q:w=0.9:g=0.6,equalizer=f=900:t=q:w=0.85:g=1.6,treble=g=1.2:f=7500:w=0.6" diff --git a/pop/sailor-song/c/sailorremix.c b/pop/sailor-song/c/sailorremix.c index 0eeb5cf688..c93c8a11cb 100644 --- a/pop/sailor-song/c/sailorremix.c +++ b/pop/sailor-song/c/sailorremix.c @@ -90,7 +90,7 @@ #include "sailor-chart.h" #define SR 48000 -#define VERSION "v118" +#define VERSION "v119" #define EAGER 0.009 // v73: "eager" placement — percussion pushes ~9 ms ahead of the grid #define PIANO_ON 1 // v112: ON — "I wanted the piano" (v71 had turned it off) #define SHIFT27 0.0 // v53: how much earlier everything after bar 27 plays, now that the regularizer fits it to four beats @@ -1021,6 +1021,9 @@ int main(void) { bedroom_level(>r, bar_n(16)->t); // v6: until bar 16 Stereo gtrRaw = { (float *)malloc(gtr.n * sizeof(float)), (float *)malloc(gtr.n * sizeof(float)), gtr.n }; // v78: her guitar as the phone heard it memcpy(gtrRaw.L, gtr.L, gtr.n * sizeof(float)); memcpy(gtrRaw.R, gtr.R, gtr.n * sizeof(float)); + // v119: the stems end at 174.27 s mid-noise (her hands on the camera) — a 20 ms fade at each stem's end, no hard cut + { Stereo *se[3] = { &vox, >r, >rRaw }; long fl = (long)(0.02 * SR); + for (int q = 0; q < 3; q++) { Stereo *st = se[q]; if (!st->L) continue; for (long i = 0; i < fl && i < st->n; i++) { float w = (float)i / fl; st->L[st->n - 1 - i] *= w; if (st->R) st->R[st->n - 1 - i] *= w; } } } pop_guitar(>r, 1.5); pop_guitar(&acg, 1.3); // v106: PRESENCE — a nylon guitar on a phone has little above 6 kHz, so pop_guitar's 10 kHz shelf lifts almost nothing; her // strum's brightness lives at 2–5 kHz. +5 dB high shelf from 2.5 kHz on her guitar, all song (the opening is this guitar alone) @@ -1231,7 +1234,7 @@ int main(void) { // break, a fem bell an 8th behind on the chord's top tones, ringing out: her playing trails into bells if ((b->n >= 66 && b->n <= 72) && nb > 3) { int tops[4]; int nt = 0; for (int m = 83; m >= 72 && nt < 4; m--) { int pc = m % 12; if (pc == pcs[0] || pc == pcs[1] || pc == pcs[2]) tops[nt++] = m; } const double pts[4] = { bt[0], MID(1), bt[2], MID(3) }; const double g = 0.13 * (b->n <= 67 ? 0.8 : 1.0) * evo(b->n, 66, 69); - for (int q = 0; q < 4; q++) { double t = pts[q] + beatDur / 2; sine(melM, NULL, hum_t(t), 0.55, tops[q % nt], hum_g(g) * 1.6, 0, 0.003, 0.45, 2); ev(t, "ping", 0.55, g, tops[q % nt]); } } // v117: sine pings, not FEM bells — "like rubber under the vocals" + for (int q = 0; q < 4; q++) { double t = pts[q] + beatDur / 2; sine(melM, NULL, hum_t(t), 0.55, tops[q % nt], hum_g(g) * 0.9, 0, 0.003, 0.45, 2); ev(t, "ping", 0.55, g, tops[q % nt]); } } // v119: ×0.9 (was 1.6) — several reached her level under the held long // v117: sine pings, not FEM bells — "like rubber under the vocals" // v24: the wub on her root, two octaves under the sub, one sweep per beat (8ths from the bridge) if (WUB[s] > 0 && !release) { wub(b->t, b->t + b->dur, ROOT[b->chord] - 24, beatDur, WUB_RATE[s], WUB[s] * hill * (s == BRIDGE && b->n < 77 ? 0.5 : 1)); ev(b->t, "wub", b->dur, WUB[s], ROOT[b->chord] - 24); } // v62: THE PIANO — jazz voicings from her cycle: G#m9 (G# B D# F# A#), Emaj7 (E G# B D#) on her Emaj7/G# bar, Bmaj9 @@ -1746,7 +1749,7 @@ int main(void) { // v109: THE TILT — "some of the audio mix seems muffled": measured over chorus 1 the mix had nearly all its energy under // 200 Hz, 1–4 kHz −10 dB and 4–10 kHz −16.5 dB against the total. A master tilt before the print: low shelf −2.5 dB at 160 Hz, // presence shelf +2.5 dB from 3.2 kHz, air +1.5 dB from 9 kHz - { Biquad lsL = bq(3, 160, 0.7, -2.5), lsR = bq(3, 160, 0.7, -2.5), hsL = bq(2, 3200, 0.7, 2.5), hsR = bq(2, 3200, 0.7, 2.5), aL = bq(2, 9000, 0.7, 1.5), aR = bq(2, 9000, 0.7, 1.5); + { Biquad lsL = bq(3, 160, 0.7, -3.5), lsR = bq(3, 160, 0.7, -3.5), /* v119: −3.5 (was −2.5) — one 52 Hz line was 40 % of all energy */ hsL = bq(2, 3200, 0.7, 2.5), hsR = bq(2, 3200, 0.7, 2.5), aL = bq(2, 9000, 0.7, 1.5), aR = bq(2, 9000, 0.7, 1.5); for (long i = 0; i < N; i++) { L[i] = (float)bq_run(&aL, bq_run(&hsL, bq_run(&lsL, L[i]))); R[i] = (float)bq_run(&aR, bq_run(&hsR, bq_run(&lsR, R[i]))); } } // v60: THE WAX PRINT — pop/lib/substrate.mjs "vinyl": tube drive 1.8 (tanh, normalized), an even-harmonic bias, a soft hiss // v60b: "things are maxing out" — the sum is brought to a 0.5 peak BEFORE the print (it was going in hot), drive 1.2 (was 1.8) -- 2.51.2