From 6cb355e6b0f003877ddbbca996d74525b7441369 Mon Sep 17 00:00:00 2001 From: type-two Date: Tue, 14 Jul 2026 13:46:41 +1000 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=BC=20revelation:=20TESTAMENT=20?= =?UTF-8?q?=E2=80=94=20record=20what=20god=20does=20(Stage=201T)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A MediaRecorder tapped off N.lim (the true final mix — post-squash, post-limiter). Lazy dest+recorder built on first roll, 1s timeslices. The #testament ⏺ button beside ♪ turns red and counts m:ss while rolling; on stop it downloads godstrument-testament-. and tickers "it is written". A ctxItem mirrors it near GODSONIQ. testamentStart/Stop/State exposed on the Synth return. Verified: button/timer/ticker/download all fire, blob is valid opus decoding to the right duration, and an oscillator probe proved the record→decode pipeline captures real audio (RMS 0.35). The Synth take is silent only because the headless preview suspends its AudioContext — the tap is correct; on a real machine it records what sounds. Co-Authored-By: Claude Fable 5 --- viz/index.html | 70 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/viz/index.html b/viz/index.html index 13770d9..c5e3992 100644 --- a/viz/index.html +++ b/viz/index.html @@ -378,6 +378,10 @@ #godspeakrec.on { display: flex; animation: gspk-pulse 1.1s ease-in-out infinite; } @keyframes gspk-pulse { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } } + /* 📼 TESTAMENT — the record button; red + gently pulsing while it bears witness */ + #testament.on { color: #ff8a8a !important; border-color: rgba(240,110,110,0.6) !important; + background: rgba(230,90,90,0.2) !important; animation: gspk-pulse 1.5s ease-in-out infinite; font-variant-numeric: tabular-nums; } + /* right-click context menu — every setting, on everything */ #ctxmenu { position: fixed; z-index: 30; display: none; width: 240px; max-height: 72vh; @@ -7797,6 +7801,8 @@ eventTicker.unshift({ text: ok ? "🎛 GODSONIQ — sampling the world for 3s… then every pluck replays it, pitched across the keys (🎸 string voice to exit)" : "🎛 GODSONIQ needs the audio worklet — press ♪ first, then try again", tAdded: now(), src: "sys" }); }); ctxItem("🌾 AMPLER — there is ample (sample the world into the kit)", () => openAmpler()); + ctxItem((Synth.testamentState && Synth.testamentState().on) ? "📼 TESTAMENT — stop & save the take" : "📼 TESTAMENT — record what god does", + () => { if (Synth.testament) Synth.testament(); }); ctxItem("⚡ GODSPEED — hold a chord, the machine runs", () => openGodspeed()); ctxItem("🌐 " + (Synth.feeding() ? "tab feed: live — click to stop" : "feed a tab (YouTube, a stream…) into the world"), async () => { const r = await Synth.feedTab(); @@ -9202,7 +9208,7 @@ g.connect(pan); pan.connect(drumBus); drums[dv] = { g, pan }; } - return { out, squash, squashMakeup, preSat, glitch, revSend, fb, satDry, satWet, leadA, leadB, morphA, morphB, leadFilt, leadAmp, + return { out, squash, squashMakeup, lim, preSat, glitch, revSend, fb, satDry, satWet, leadA, leadB, morphA, morphB, leadFilt, leadAmp, bassOsc, bassAmp, padOscs, padFilt, padAmp, droneOscs, droneAmp, echoDelay, echoFilt, echoFb, echoWet, echoFlutAmt, hissGain, chorusWet, drumBus, drums, noiseBuf, duckG }; @@ -9626,7 +9632,69 @@ } }); + // ---- 📼 TESTAMENT — record what god does ("it is written") -------------- + // A MediaRecorder tapped off N.lim: the TRUE final mix, post-squash and + // post-limiter — exactly what you hear. Lazy (destination + recorder built + // on first roll), timesliced so a crash loses ≤1s. The GODSONIQ worklet + // Recorder is a different machine for a different purpose — untouched. + let testDest = null, testRec = null, testChunks = [], testT0 = 0, testUiTimer = null; + function testamentStart() { + if (!ctx) start(); // wake the graph — a recording can begin the session + if (testRec) return false; // already rolling + if (!testDest) { testDest = ctx.createMediaStreamDestination(); N.lim.connect(testDest); } + const mime = ["audio/webm;codecs=opus", "audio/webm", "audio/mp4"] + .find(m => window.MediaRecorder && MediaRecorder.isTypeSupported(m)); + if (!mime) return false; // no MediaRecorder / no supported container (old Safari) + testChunks = []; testRec = new MediaRecorder(testDest.stream, { mimeType: mime, audioBitsPerSecond: 256000 }); + testRec.ondataavailable = (e) => { if (e.data && e.data.size) testChunks.push(e.data); }; + testRec.start(1000); // 1s timeslices + testT0 = ctx.currentTime; return true; + } + function testamentStop() { + return new Promise((resolve) => { + if (!testRec) { resolve(null); return; } + const rec = testRec, secs = ctx.currentTime - testT0; + const ext = rec.mimeType.indexOf("mp4") >= 0 ? "m4a" : "webm"; + rec.onstop = () => { const blob = new Blob(testChunks, { type: rec.mimeType }); testChunks = []; resolve({ blob, secs, ext }); }; + testRec = null; rec.stop(); + }); + } + function testamentState() { return { on: !!testRec, secs: testRec ? ctx.currentTime - testT0 : 0 }; } + + const testBtn = document.createElement("div"); + testBtn.id = "testament"; testBtn.textContent = "⏺"; testBtn.title = "TESTAMENT — record the master mix to a file"; + Object.assign(testBtn.style, { position: "fixed", top: "12px", right: "132px", zIndex: 10, + height: "30px", minWidth: "30px", padding: "0 7px", borderRadius: "8px", display: "flex", + alignItems: "center", justifyContent: "center", background: "rgba(20,28,44,0.7)", color: "#cba6a6", + cursor: "pointer", fontSize: "13px", border: "1px solid rgba(120,150,200,0.25)", + backdropFilter: "blur(6px)", WebkitBackdropFilter: "blur(6px)" }); + if (!AC || !window.MediaRecorder) { testBtn.style.opacity = "0.3"; testBtn.title = "this browser cannot record"; } + document.body.appendChild(testBtn); + const testFmt = (s) => { s = Math.max(0, Math.floor(s)); return Math.floor(s / 60) + ":" + String(s % 60).padStart(2, "0"); }; + function testPaint() { const st = testamentState(); testBtn.classList.toggle("on", st.on); testBtn.textContent = st.on ? "⏺ " + testFmt(st.secs) : "⏺"; } + function testamentToggle() { + if (testamentState().on) { + testamentStop().then((r) => { + if (testUiTimer) { clearInterval(testUiTimer); testUiTimer = null; } + testPaint(); + if (!r || !r.blob || !r.blob.size) return; + const d = new Date(), pad = (n) => String(n).padStart(2, "0"); + const stamp = "" + d.getFullYear() + pad(d.getMonth() + 1) + pad(d.getDate()) + "-" + pad(d.getHours()) + pad(d.getMinutes()); + const a = document.createElement("a"); a.href = URL.createObjectURL(r.blob); + a.download = "godstrument-testament-" + stamp + "." + r.ext; + document.body.appendChild(a); a.click(); document.body.removeChild(a); + setTimeout(() => URL.revokeObjectURL(a.href), 4000); + eventTicker.unshift({ text: "📼 testament: " + testFmt(r.secs) + " — it is written", tAdded: now(), src: "sys" }); + }); + } else { + if (!testamentStart()) { eventTicker.unshift({ text: "📼 this browser cannot bear witness", tAdded: now(), src: "sys" }); return; } + testPaint(); testUiTimer = setInterval(testPaint, 1000); + } + } + testBtn.addEventListener("click", testamentToggle); + return { update(dests) { if (dests) Object.assign(D, dests); }, pluck, + testamentStart, testamentStop, testamentState, testament: testamentToggle, toggle() { on ? stop() : start(); paint(); return on; }, playing() { return on; }, stringVoice(v) { if (v === undefined) return ksMode; if (!ctx) start(); ksMode = !!v; sampleMode = false; return ksMode; }, godsoniq(secs) { return godsoniq(secs); }, sampling() { return sampleMode && !!sampleBuf; },