🎛 make wavetable.morph & granular.density literal (honesty fix)
The last two names that lied are now true:
- wavetable.morph — was a filter-Q sweep. Now a REAL wavetable: the lead is two
band-limited PeriodicWaves (a mellow one + a bright saw-rich one) crossfaded by
morph. Spectrally verified: smooth brightness sweep (centroid 462→880→1174
across morph 0→0.5→1), no aliasing.
- granular.density — was one occasional noise burst every 4th step. Now a REAL
grain cloud: a density-scaled scatter of short (12–40ms) pitch-varied grains
per step (0 at rest → a dense cloud when the world writes). noiseHit takes a
scheduled start time so grains scatter within the step.
The grimoire's existing claims ("sweeps the oscillator through its bank of
waveforms" / "a cloud of tiny grains") were aspirational — now they're accurate,
so no doc softening needed. Every clickable/routable thing now does what it says.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
adde3bc326
commit
b884cf9642
@ -6793,11 +6793,18 @@
|
||||
lfo = ctx.createOscillator(); lfo.frequency.value = 0.2;
|
||||
const lfoAmt = ctx.createGain(); lfoAmt.gain.value = 260; lfo.connect(lfoAmt);
|
||||
|
||||
const leadOsc = ctx.createOscillator(); leadOsc.type = "sawtooth";
|
||||
// real wavetable lead: crossfade a mellow wave ↔ a bright (saw-rich) wave by wavetable.morph
|
||||
const leadFilt = ctx.createBiquadFilter(); leadFilt.type = "lowpass"; leadFilt.frequency.value = 1200;
|
||||
const leadAmp = ctx.createGain(); leadAmp.gain.value = 0;
|
||||
leadOsc.connect(leadFilt); leadFilt.connect(leadAmp); leadAmp.connect(preSat);
|
||||
lfoAmt.connect(leadFilt.frequency); leadOsc.start();
|
||||
const _nH = 20, _rA = new Float32Array(_nH), _iA = new Float32Array(_nH), _rB = new Float32Array(_nH), _iB = new Float32Array(_nH);
|
||||
for (let n = 1; n < _nH; n++) { _iA[n] = n <= 4 ? 0.9 / n : 0.03 / n; _iB[n] = 1 / n; } // A mellow · B bright
|
||||
const leadA = ctx.createOscillator(); leadA.setPeriodicWave(ctx.createPeriodicWave(_rA, _iA));
|
||||
const leadB = ctx.createOscillator(); leadB.setPeriodicWave(ctx.createPeriodicWave(_rB, _iB));
|
||||
const morphA = ctx.createGain(); morphA.gain.value = 1;
|
||||
const morphB = ctx.createGain(); morphB.gain.value = 0;
|
||||
leadA.connect(morphA); leadB.connect(morphB); morphA.connect(leadFilt); morphB.connect(leadFilt);
|
||||
leadFilt.connect(leadAmp); leadAmp.connect(preSat);
|
||||
lfoAmt.connect(leadFilt.frequency); leadA.start(); leadB.start();
|
||||
|
||||
const bassOsc = ctx.createOscillator(); bassOsc.type = "square";
|
||||
const bassFilt = ctx.createBiquadFilter(); bassFilt.type = "lowpass"; bassFilt.frequency.value = 500;
|
||||
@ -6846,7 +6853,7 @@
|
||||
schuDrone.connect(schuDroneG); schuDroneG.connect(droneFilt); schuDrone.start();
|
||||
|
||||
lfo.start();
|
||||
return { out, preSat, glitch, revSend, fb, satDry, satWet, leadOsc, leadFilt, leadAmp,
|
||||
return { out, 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 };
|
||||
}
|
||||
@ -6858,19 +6865,20 @@
|
||||
g.linearRampToValueAtTime(peak, t + 0.008);
|
||||
g.setTargetAtTime(peak * 0.28, t + 0.01, dec);
|
||||
}
|
||||
function noiseHit(freq, dur, vol) {
|
||||
function noiseHit(freq, dur, vol, at) {
|
||||
const n = Math.floor(ctx.sampleRate * dur), b = ctx.createBuffer(1, n, ctx.sampleRate), ch = b.getChannelData(0);
|
||||
for (let i = 0; i < n; i++) ch[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / n, 3);
|
||||
const s = ctx.createBufferSource(); s.buffer = b;
|
||||
const hp = ctx.createBiquadFilter(); hp.type = "highpass"; hp.frequency.value = freq;
|
||||
const g = ctx.createGain(); g.gain.value = vol;
|
||||
s.connect(hp); hp.connect(g); g.connect(N.preSat); s.start();
|
||||
s.connect(hp); hp.connect(g); g.connect(N.preSat); s.start(at != null ? at : ctx.currentTime);
|
||||
}
|
||||
|
||||
function params() {
|
||||
const t = ctx.currentTime, S = (p, v, tc) => p.setTargetAtTime(v, t, tc || 0.1);
|
||||
S(N.leadFilt.frequency, 250 + d("filter.cutoff", 0.3) * 5000, 0.05);
|
||||
N.leadFilt.Q.setTargetAtTime(1 + d("wavetable.morph", 0) * 8, t, 0.1);
|
||||
const _wm = clamp(d("wavetable.morph", 0), 0, 1); // real wavetable: crossfade mellow ↔ bright
|
||||
S(N.morphA.gain, 1 - _wm, 0.08); S(N.morphB.gain, _wm, 0.08);
|
||||
S(N.padFilt.frequency, 220 + d("pad.brightness", 0) * 4200);
|
||||
S(N.padAmp.gain, d("pad.brightness", 0) * 0.07, 0.3); // ×9 detuned saws → trimmed to hold the level
|
||||
if (crusher) { // world-driven 12-bit-DAC crunch (default clean)
|
||||
@ -6899,7 +6907,7 @@
|
||||
const minGap = 30 / Math.max(40, godtime.bpm); // one 8th note
|
||||
const ln = Math.round(d("lead.note", 0));
|
||||
if (ln >= 24 && ln !== leadNote) {
|
||||
leadNote = ln; N.leadOsc.frequency.setTargetAtTime(mtof(ln), t, 0.02);
|
||||
leadNote = ln; N.leadA.frequency.setTargetAtTime(mtof(ln), t, 0.02); N.leadB.frequency.setTargetAtTime(mtof(ln), t, 0.02);
|
||||
if (t - lastLeadT > minGap) { hit(N.leadAmp, 0.13, 0.35); lastLeadT = t; }
|
||||
}
|
||||
const bn = Math.round(d("bass.note", 0));
|
||||
@ -6919,7 +6927,9 @@
|
||||
while (nextAt < now + 0.1) {
|
||||
const t = Math.max(nextAt, now), dens = d("perc.density", 0);
|
||||
if (Math.random() < dens * 0.9) noiseHit(7000, 0.05, 0.06 + dens * 0.07);
|
||||
if (step % 4 === 0 && Math.random() < 0.3 + d("granular.density", 0) * 0.4) noiseHit(2800, 0.03, 0.04);
|
||||
const _gd = d("granular.density", 0); // real granular: a cloud of short scattered grains
|
||||
for (let _g = 0, _ng = Math.floor(_gd * _gd * 6); _g < _ng; _g++)
|
||||
noiseHit(1400 + Math.random() * 5000, 0.012 + Math.random() * 0.028, 0.02 + _gd * 0.03, t + Math.random() * six);
|
||||
if (Math.random() < d("glitch", 0) * 0.18) {
|
||||
const g = N.glitch.gain; g.setValueAtTime(1, t); g.setValueAtTime(0, t + 0.02); g.setValueAtTime(1, t + 0.055);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user