🎛 GODSONIQ — resample the world onto the keys (ASR-10 nod)

Record ~3s of the live master mix into a buffer via a recorder AudioWorklet
tapping the full post-FX bus, then replay it pitched across the keyboard
(playbackRate = 2^((midi-60)/12), middle C = unity). Runs through the same
FX chain as every voice, so `crush` gives it the classic 12-bit sampler grit.
Right-click sky → 🎛 GODSONIQ to sample / re-sample; 🎸 string voice exits
back to the FM tine. Capture path verified: full-amplitude sawtooth recorded
clean (peak 0.98, rms 0.49) over the realtime port round-trip.

Grimoire (truth) + manual Appendix J updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-10 09:30:25 +10:00
parent b884cf9642
commit 5ffa3a4bfb
2 changed files with 42 additions and 2 deletions

View File

@ -586,6 +586,8 @@ The matrix pours out clean control values whether or not anyone is listening. Ch
**The built-in voice, up close.** Press `♪` and the browser synth wakes. Its plucked notes ring with a **DX7-style FM tine** — brighter the harder they're struck; its pads bloom into a **detuned supersaw**; a **Juno chorus** warms the whole thing. Prefer strings? Right-click the sky → **🎸 string voice** and every pluck becomes a real **Karplus-Strong** plucked string. And route anything to `crush` for a **12-bit-DAC bitcrusher** — the planet degrading its own converter into lo-fi grit. (Every one of these was tuned by spectral analysis before it shipped — rendered offline and measured, so it's in-tune and clean, not just plausible.)
**🎛 GODSONIQ — resample the world onto the keys.** A cheeky nod to the Ensoniq ASR-10: right-click the sky → **🎛 GODSONIQ** and it records ~3 seconds of the *live* master mix — whatever the world is playing right now, this exact never-repeating take — into a buffer. From then on every pluck **replays that capture, pitched across the keyboard** (middle C plays it at speed, an octave up plays it twice as fast). It runs through the full FX chain, so route `crush` at it and you get the classic 12-bit sampler grit. Right-click → 🎛 again to grab a fresh slice; **🎸 string voice** (or toggling it off) drops you back to the FM tine. The browser-native bounce: freeze a moment of the planet, then play the planet like an instrument.
**Zero — the moods, the gentle front door.** On your very first visit the instrument greets you with **🌅 first light** instead of the full flood: a bass line, soft hats, a small melody, modest space — a thing shaped like a song, with the world still playing underneath it. Right-click the sky → **🌗 mood** to move between *first light*, *ambient drift* (no drums, all atmosphere, the planet as weather), and **the full storm** — the factory instrument with every one of the world's hands on it at once. Ease in, then open the door.
**One — the built-in browser synth.** Click **♪** and a full voice rack wakes inside the page: **lead** (bitcoin's price and your market's average price both quantize to it — minor pentatonic and G dorian), **bass** (quake depth chooses its notes in a low minor), **pad** (brightened by Tokyo's warmth, daylight, harmony, the birth rate, longer lives), **drone** (thickened by aircraft aloft, barometric pressure, Saturn's slow wheel, the weight of the national debt), and **perc** (BTC volatility and market activity drive the hats). Over them sit the effects the world modulates: **reverb** (earthquakes open the void, room light sizes it), **delay** (your hand's Y feeds it, Mercury retrograde smears it), **saturation** (Delhi's air becomes grit, hard aspects add dissonance, wildfires burn it, hunger grinds beneath), and **glitch** (every Wikipedia edit on Earth, every light flash, every new wildfire fires a burst). Nothing to install; the planet plays a synth in your tab.
@ -1013,3 +1015,7 @@ Every feed the world sends is one of eight temperaments; the shape dictates how
- **🎹 Supersaw pad** — each of the pad's chord tones is a detuned 9-saw cluster (JP-8000 style) for a lush, wide wall.
- **🎸 String voice** — right-click the sky → *string voice* swaps the FM tine for a real Karplus-Strong plucked string (an AudioWorklet: noise burst → damped delay loop, sample-accurate). Spectrally verified stable and in-tune.
- **🔊 12-bit bitcrush** — a world-drivable lo-fi stage on the synth's master bus (an AudioWorklet doing true bit-depth quantise + sample-rate reduction). Route anything to `crush` and the planet degrades its own DAC — clean by default (16-bit).
- **⬗ Parameter locks (p-locks)** — Pocket-Operator / Elektron style: a texture voice (filter, glitch, reverb, drive) gets a per-step `lock` lane that pins its value to a fixed level on that step (blocky, disjointed), overriding the world; drag a step below the lane to unlock it and hand that step back to the data. Applied in `computeDests` via `applyGroove`; travels with the patch.
- **🌊 Juno-106 chorus** — an always-on subtle BBD-style stereo chorus on the synth's master bus (two short delays warbled by slow LFOs, panned wide) that warms and widens the dry oscillators.
- **🔊 12-bit bitcrush** — a world-drivable lo-fi stage on the synth's master bus (an AudioWorklet doing true bit-depth quantise + sample-rate reduction). Route anything to `crush` and the planet degrades its own DAC — clean by default (16-bit).
- **🎛 GODSONIQ (resampler)** — a cheeky nod to the Ensoniq ASR-10: right-click the sky → *GODSONIQ* records ~3s of the live master mix (a recorder AudioWorklet taps the full post-FX bus) into a buffer, then every pluck replays that capture pitched across the keys (playbackRate = 2^((midi-60)/12), middle C = unity). Plays through the full FX chain, so `crush` gives it the classic 12-bit sampler grit. 🎸 string voice exits back to the FM tine. The browser-native bounce — capture the world's never-repeating take, then play it.

View File

@ -1018,6 +1018,8 @@
<p><b>The built-in voice, up close.</b> Press <kbd></kbd> and the browser synth wakes. Its plucked notes ring with a <b>DX7-style FM tine</b> — brighter the harder they're struck; its pads bloom into a <b>detuned supersaw</b>; a <b>Juno chorus</b> warms the whole thing. Prefer strings? Right-click the sky → <b>🎸 string voice</b> and every pluck becomes a real <b>Karplus-Strong</b> plucked string. And route anything to <kbd>crush</kbd> for a <b>12-bit-DAC bitcrusher</b> — the planet degrading its own converter into lo-fi grit. (Every one of these was tuned by spectral analysis before it shipped — rendered offline and measured, so it's in-tune and clean, not just plausible.)</p>
<p><b>🎛 GODSONIQ — resample the world onto the keys.</b> A cheeky nod to the Ensoniq ASR-10: right-click the sky → <b>🎛 GODSONIQ</b> and it records ~3 seconds of the <em>live</em> master mix — whatever the world is playing right now, this exact never-repeating take — into a buffer. From then on every pluck <b>replays that capture, pitched across the keyboard</b> (middle C plays it at speed, an octave up plays it twice as fast). It runs through the full FX chain, so route <kbd>crush</kbd> at it and you get the classic 12-bit sampler grit. Right-click → 🎛 again to grab a fresh slice; <b>🎸 string voice</b> (or toggling it off) drops you back to the FM tine. The browser-native bounce: freeze a moment of the planet, then play the planet like an instrument.</p>
<p><b>Zero — the moods, the gentle front door.</b> On your very first visit the instrument greets you with <b>🌅 first light</b> instead of the full flood: a bass line, soft hats, a small melody, modest space — a thing shaped like a song, with the world still playing underneath it. Right-click the sky → <b>🌗 mood</b> to move between <i>first light</i>, <i>ambient drift</i> (no drums, all atmosphere, the planet as weather), and <b>the full storm</b> — the factory instrument with every one of the world's hands on it at once. Ease in, then open the door.</p>
<p><b>One — the built-in browser synth.</b> Click <b></b> and a full voice rack wakes inside the page: <b>lead</b> (bitcoin's price and your market's average price both quantize to it — minor pentatonic and G dorian), <b>bass</b> (quake depth chooses its notes in a low minor), <b>pad</b> (brightened by Tokyo's warmth, daylight, harmony, the birth rate, longer lives), <b>drone</b> (thickened by aircraft aloft, barometric pressure, Saturn's slow wheel, the weight of the national debt), and <b>perc</b> (BTC volatility and market activity drive the hats). Over them sit the effects the world modulates: <b>reverb</b> (earthquakes open the void, room light sizes it), <b>delay</b> (your hand's Y feeds it, Mercury retrograde smears it), <b>saturation</b> (Delhi's air becomes grit, hard aspects add dissonance, wildfires burn it, hunger grinds beneath), and <b>glitch</b> (every Wikipedia edit on Earth, every light flash, every new wildfire fires a burst). Nothing to install; the planet plays a synth in your tab.</p>
@ -5536,6 +5538,10 @@
() => earthEchoSet(!earthEchoOn));
ctxItem("🎸 string voice" + (Synth.stringVoice() ? " (on — plucks ring like a string)" : " — swap the FM tine for a Karplus string"),
() => Synth.stringVoice(!Synth.stringVoice()));
ctxItem("🎛 GODSONIQ" + (Synth.sampling() ? " (on — plucks replay your capture · tap to re-sample)" : " — resample the world onto the keys"),
() => { const ok = Synth.godsoniq(3);
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("⌨ keys — remap the keyboard", () => openKeys());
if (commune.room) {
const gOwner = commune.alloc["@godtime"];
@ -6942,8 +6948,9 @@
// drivable) and a sample-accurate Karplus-Strong string. Both need a worklet
// (native feedback + real sample-rate reduction are unstable/impossible with
// stock nodes — the spectral harness proved it). Loaded async from a Blob URL.
const WORKLET_SRC = "class Bitcrush extends AudioWorkletProcessor{static get parameterDescriptors(){return[{name:'bits',defaultValue:16,minValue:1,maxValue:16,automationRate:'k-rate'},{name:'reduction',defaultValue:1,minValue:1,maxValue:64,automationRate:'k-rate'}];}constructor(){super();this.phase=0;this.hold=null;}process(inputs,outputs,p){const inp=inputs[0],out=outputs[0];if(!inp||!inp.length)return true;const b=p.bits[0],red=Math.max(1,Math.round(p.reduction[0])),lv=Math.pow(2,b),nC=out.length,nS=out[0].length;if(!this.hold)this.hold=new Float32Array(8);for(let i=0;i<nS;i++){if(this.phase<=0){for(let c=0;c<nC;c++){const xi=(inp[c]||inp[0])[i];this.hold[c]=Math.round(xi*lv)/lv;}this.phase=red;}this.phase--;for(let c=0;c<nC;c++)out[c][i]=this.hold[c];}return true;}}registerProcessor('bitcrush',Bitcrush);class Karplus extends AudioWorkletProcessor{constructor(){super();this.v=[];for(let i=0;i<8;i++)this.v.push({buf:new Float32Array(4096),len:0,idx:0,active:false,fb:0.995,damp:0.5,last:0,amp:0});this.n=0;this.port.onmessage=(e)=>{const vo=this.v[this.n];this.n=(this.n+1)%8;const L=Math.max(2,Math.min(4096,Math.round(sampleRate/e.data.freq)));vo.len=L;vo.idx=0;vo.active=true;vo.amp=e.data.vel;vo.last=0;for(let i=0;i<L;i++)vo.buf[i]=Math.random()*2-1;};}process(inputs,outputs){const out=outputs[0],y=out[0],nS=y.length;for(let i=0;i<nS;i++)y[i]=0;for(const vo of this.v){if(!vo.active)continue;for(let i=0;i<nS;i++){const cur=vo.buf[vo.idx];const f=vo.damp*cur+(1-vo.damp)*vo.last;vo.last=f;vo.buf[vo.idx]=f*vo.fb;y[i]+=cur*vo.amp*0.4;vo.idx++;if(vo.idx>=vo.len)vo.idx=0;}}for(let c=1;c<out.length;c++)out[c].set(y);return true;}}registerProcessor('karplus',Karplus);";
const WORKLET_SRC = "class Bitcrush extends AudioWorkletProcessor{static get parameterDescriptors(){return[{name:'bits',defaultValue:16,minValue:1,maxValue:16,automationRate:'k-rate'},{name:'reduction',defaultValue:1,minValue:1,maxValue:64,automationRate:'k-rate'}];}constructor(){super();this.phase=0;this.hold=null;}process(inputs,outputs,p){const inp=inputs[0],out=outputs[0];if(!inp||!inp.length)return true;const b=p.bits[0],red=Math.max(1,Math.round(p.reduction[0])),lv=Math.pow(2,b),nC=out.length,nS=out[0].length;if(!this.hold)this.hold=new Float32Array(8);for(let i=0;i<nS;i++){if(this.phase<=0){for(let c=0;c<nC;c++){const xi=(inp[c]||inp[0])[i];this.hold[c]=Math.round(xi*lv)/lv;}this.phase=red;}this.phase--;for(let c=0;c<nC;c++)out[c][i]=this.hold[c];}return true;}}registerProcessor('bitcrush',Bitcrush);class Karplus extends AudioWorkletProcessor{constructor(){super();this.v=[];for(let i=0;i<8;i++)this.v.push({buf:new Float32Array(4096),len:0,idx:0,active:false,fb:0.995,damp:0.5,last:0,amp:0});this.n=0;this.port.onmessage=(e)=>{const vo=this.v[this.n];this.n=(this.n+1)%8;const L=Math.max(2,Math.min(4096,Math.round(sampleRate/e.data.freq)));vo.len=L;vo.idx=0;vo.active=true;vo.amp=e.data.vel;vo.last=0;for(let i=0;i<L;i++)vo.buf[i]=Math.random()*2-1;};}process(inputs,outputs){const out=outputs[0],y=out[0],nS=y.length;for(let i=0;i<nS;i++)y[i]=0;for(const vo of this.v){if(!vo.active)continue;for(let i=0;i<nS;i++){const cur=vo.buf[vo.idx];const f=vo.damp*cur+(1-vo.damp)*vo.last;vo.last=f;vo.buf[vo.idx]=f*vo.fb;y[i]+=cur*vo.amp*0.4;vo.idx++;if(vo.idx>=vo.len)vo.idx=0;}}for(let c=1;c<out.length;c++)out[c].set(y);return true;}}registerProcessor('karplus',Karplus);class Recorder extends AudioWorkletProcessor{constructor(){super();this.rec=false;this.buf=null;this.pos=0;this.port.onmessage=(e)=>{if(e.data.rec){this.buf=new Float32Array(e.data.len);this.pos=0;this.rec=true;}};}process(inputs){const inp=inputs[0];if(this.rec&&inp&&inp[0]){const x=inp[0];for(let i=0;i<x.length&&this.pos<this.buf.length;i++)this.buf[this.pos++]=x[i];if(this.pos>=this.buf.length){this.rec=false;const b=this.buf;this.buf=null;this.port.postMessage({done:true,buf:b},[b.buffer]);}}return true;}}registerProcessor('recorder',Recorder);";
let crusher = null, ksNode = null, ksMode = false, workletTried = false;
let recNode = null, sampleBuf = null, sampleMode = false; // GODSONIQ (cheeky Ensoniq nod): resample the world onto the keys
function loadWorklets() {
if (workletTried || !ctx || !ctx.audioWorklet || !N) return;
workletTried = true;
@ -6952,8 +6959,24 @@
crusher = new AudioWorkletNode(ctx, "bitcrush");
N.glitch.disconnect(); N.glitch.connect(crusher); crusher.connect(N.out); // splice onto the master bus
ksNode = new AudioWorkletNode(ctx, "karplus"); ksNode.connect(N.preSat);
// GODSONIQ recorder: tap the full master mix (post-FX, post-crush). Pulled via a
// muted sink so it processes every quantum; records N samples on cue, ships the buffer back.
recNode = new AudioWorkletNode(ctx, "recorder", { numberOfInputs: 1, numberOfOutputs: 1, outputChannelCount: [1] });
N.out.connect(recNode);
const rsink = ctx.createGain(); rsink.gain.value = 0; recNode.connect(rsink); rsink.connect(ctx.destination);
recNode.port.onmessage = (e) => { if (!e.data.done) return;
const f = e.data.buf, ab = ctx.createBuffer(1, f.length, ctx.sampleRate); ab.copyToChannel(f, 0);
sampleBuf = ab; sampleMode = true; ksMode = false; // captured — plucks now replay it, pitched
};
}).catch(() => { workletTried = false; });
}
function godsoniq(secs) { // arm the resampler for `secs` of the live world
if (!ctx) start();
if (!recNode) return false; // worklet not ready
if (!on) start(); // world must be sounding to sample it
recNode.port.postMessage({ rec: true, len: Math.floor((secs || 3) * ctx.sampleRate) });
return true;
}
function start() {
if (!AC) return;
if (!ctx) { ctx = new AC(); N = build(); loadWorklets(); }
@ -6976,6 +6999,16 @@
if (!on) N.out.gain.setTargetAtTime(0.85, t, 0.1); // master up; data voices stay silent
const v = vel == null ? 0.8 : vel;
const freq = mtof(midi);
if (sampleMode && sampleBuf) { // GODSONIQ — replay the captured world, pitched across the keys
const src = ctx.createBufferSource(); src.buffer = sampleBuf;
src.playbackRate.value = Math.pow(2, (midi - 60) / 12); // note-spread from middle C (ASR-style)
const g = ctx.createGain(); g.gain.value = 0;
src.connect(g); g.connect(N.preSat); // through the full FX chain — route `crush` for the ASR grit
g.gain.setValueAtTime(0, t); g.gain.linearRampToValueAtTime(0.5 * v, t + 0.005);
g.gain.setTargetAtTime(0, t + 0.05, 0.6);
src.start(t); src.stop(t + 3.2);
return;
}
if (ksMode && ksNode) { ksNode.port.postMessage({ freq, vel: v }); return; } // Karplus string voice
// FM pluck — a sine carrier phase-modulated by a sine operator, index enveloped
@ -7019,7 +7052,8 @@
return { update(dests) { if (dests) Object.assign(D, dests); }, pluck,
toggle() { on ? stop() : start(); paint(); return on; }, playing() { return on; },
stringVoice(v) { if (v === undefined) return ksMode; if (!ctx) start(); ksMode = !!v; return ksMode; } };
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; } };
})();
// ---------------------------------------------------------------------------