mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 03:09:57 +00:00
feat(highway_3d): Butterchurn visualizer background style (#598)
* 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>
This commit is contained in:
co-authored by
Claude Opus 4.8
ChrisBeWithYou
parent
97dae88860
commit
3bb55ab854
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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);
|
||||
Reference in New Issue
Block a user