📼 revelation: TESTAMENT — record what god does (Stage 1T)

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-<stamp>.<ext> 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 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-14 13:46:41 +10:00
parent e6c464daae
commit 6cb355e6b0

View File

@ -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; },