mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 11:19:24 +00:00
* feat(highway_3d): add Butterchurn visualizer background style Adds an opt-in "Butterchurn (visualizer)" option to the 3D Highway plugin's Background-style dropdown. When selected, the highway renders over a WebGL MilkDrop (Butterchurn) canvas that reacts to your playing (guitar input on desktop, the song <audio> spectrum in the browser) and the chart (beat/note/ chord accents + instrument-color tint). The default stays 'particles', so existing users see no change until they pick it. Integrates the standalone "3D Highway + Butterchurn" mod into the bundled renderer as the 'butterchurn' bg-style (not a fork): - a self-contained _bc* controller that lazy-loads the vendored butterchurn libs only when the style is selected; mount/unmount is driven idempotently by the existing bg-style lifecycle (_bcSyncMode in _bgMountStyle) plus an explicit teardown in destroy() - the renderer uses alpha:true with the transparent clear gated on the mode, so every other bg style stays byte-identical (opaque clear) - the fog-scenery <audio> tap is disabled while active to avoid a double createMediaElementSource on #audio - the mod's slopsmith* globals are adapted to the current feedBack* names and the vendored asset URLs repointed to /api/plugins/highway_3d/assets/ Vendors butterchurn.min.js + butterchurnPresets.min.js (MIT) + viz-worklet.js under assets/vendor/; see plugins/highway_3d/NOTICE for attribution. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Q9BpGYqUaga9ZJyS3dDPq * fix(highway_3d): anchor Butterchurn panel to the highway, centered Addresses three issues found testing the visualizer control panel: - Attach the panel + preset pane to the 3D highway's `wrap` (position:absolute, pointer-events:auto) instead of position:fixed on document.body, so they sit on the highway's right edge and only exist while the highway is on-screen (no longer linger on the main menu / float at the app edge). - Re-home the singleton panel to the active highway wrap on mount, so it follows whichever highway is showing (e.g. moves off Virtuoso's embedded highway onto a normal song's highway) instead of sticking to the first one created. - Center it vertically (top:50% + translateY(-50%), folded into the slide transform) so a top overlay element no longer covers it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011Q9BpGYqUaga9ZJyS3dDPq * fix(highway_3d): harden Butterchurn audio + lifecycle (review #597) Browser audio reactivity now REUSES the highway's existing shared analyser (the fog scenery's #audio / stems side-chain tap) instead of opening a second createMediaElementSource on #audio. The old _bcBrowserSource path: - threw InvalidStateError when the fog tap already owned #audio (default config), leaving the visualizer non-reactive in the browser, and could permanently disable fog reactivity if it tapped first (one-shot/element); - rerouted the song through a fresh, possibly-suspended AudioContext, which could MUTE playback when butterchurn was selected mid-song; - ignored the stems analyser, so it saw only silence on sloppak songs. _bcCreateController now takes an audioProvider (wired to _bgGetAnalyser) and connectAudio()s the shared AnalyserNode (a passthrough, so the fog's own reads are undisturbed). Also: - destroy() now closes the AudioContext when we own it (desktop / browser fallback), fixing a per-mount leak that hit the browser ~6-context cap after a few style toggles. The shared (fog-owned) context is never closed. - _bgApplyVenueSceneFog keeps the clear transparent while butterchurn is active, so the venue scene no longer occludes the visualizer. - _bcLoadLib no longer caches a rejected promise, so a transient vendor-load failure can be retried instead of disabling the feature for the session. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(highway_3d): Butterchurn lifecycle + audio re-bind (Codex preflight) Local Codex preflight on the Butterchurn feature flagged four issues; all fixed: - WebGL context leak on teardown: destroy() (and the async-init failure path) now call _bcReleaseCanvasGL() to force WEBGL_lose_context before dropping the canvas, so repeated mount/toggle cycles can't exhaust the browser's WebGL context cap. - Stale shared analyser across songs: the browser path captured the analyser once at mount, so a sloppak stems swap (new analyser, often new context) left the visualizer reacting to a dead node. update() now compares the live _bgGetAnalyser() against what the controller actually bound (boundAnalyser(), guarded by ready()) and either reconnects (same context) or rebuilds the controller (context changed) via the proven destroy()+_bcSyncMode paths. - Half-mounted controller on createVisualizer failure: the async .catch now cleans up (closes an owned AudioContext, removes layers, marks dead) and _bcSyncMode retries when bcCtrl.dead(), instead of leaking and never recovering. - _bcFfIdx off-by-one dropped accents landing exactly on a seek/loop target time; it now uses strict < so the update walkers fire the boundary event. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: ChrisBeWithYou <christian.a.cowan@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
48 lines
2.1 KiB
JavaScript
48 lines
2.1 KiB
JavaScript
/*
|
|
* Slopsmith visualizer — audio feed AudioWorkletProcessor.
|
|
* Generates Butterchurn's drive signal off the main thread (replaces the
|
|
* deprecated ScriptProcessor): guitar PCM (gained) + a bass-band song-energy
|
|
* pulse + a mid-band chart-accent pulse. The main thread pushes the latest
|
|
* guitar frame and the song/chart/gain scalars via port messages.
|
|
*/
|
|
class VizFeedProcessor extends AudioWorkletProcessor {
|
|
constructor() {
|
|
super();
|
|
this.frame = null; // latest guitar PCM (Float32Array)
|
|
this.readIdx = 0;
|
|
this.song = 0;
|
|
this.chart = 0;
|
|
this.gain = 6;
|
|
this.phase = 0;
|
|
this.phase2 = 0;
|
|
this.osc = (2 * Math.PI * 90) / sampleRate; // ~90 Hz → bass band (song)
|
|
this.osc2 = (2 * Math.PI * 520) / sampleRate; // ~520 Hz → mid band (chart)
|
|
this.port.onmessage = (e) => {
|
|
const d = e.data;
|
|
if (!d) return;
|
|
if (d.frame) { this.frame = d.frame; this.readIdx = 0; }
|
|
if (typeof d.song === 'number') this.song = d.song;
|
|
if (typeof d.chart === 'number') this.chart = d.chart;
|
|
if (typeof d.gain === 'number') this.gain = d.gain;
|
|
};
|
|
}
|
|
process(inputs, outputs) {
|
|
const out = outputs[0] && outputs[0][0];
|
|
if (!out) return true;
|
|
const f = this.frame, fl = f ? f.length : 0;
|
|
const TWO_PI = 2 * Math.PI;
|
|
for (let i = 0; i < out.length; i++) {
|
|
const g = (fl ? f[this.readIdx % fl] : 0) * this.gain;
|
|
this.readIdx++;
|
|
const song = this.song * (0.7 * Math.sin(this.phase) + 0.3 * (Math.random() * 2 - 1)) * 1.4;
|
|
const chart = this.chart * (0.5 * Math.sin(this.phase2) + 0.5 * (Math.random() * 2 - 1)) * 1.5;
|
|
this.phase += this.osc; if (this.phase > TWO_PI) this.phase -= TWO_PI;
|
|
this.phase2 += this.osc2; if (this.phase2 > TWO_PI) this.phase2 -= TWO_PI;
|
|
const v = g + song + chart;
|
|
out[i] = v > 1 ? 1 : (v < -1 ? -1 : v);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
registerProcessor('viz-feed', VizFeedProcessor);
|