reduce waveform mem consumption

This commit is contained in:
2026-09-22 07:37:53 -04:00
parent f5f4bc73b9
commit 0640172f05

View File

@@ -13,7 +13,7 @@
* and a dotted placeholder when idle.
*/
import { createEffect, on } from "solid-js";
import { createEffect, createMemo, on, Show } from "solid-js";
import { useTerminalDimensions } from "@opentui/solid";
import { useVisualizer } from "@/stores/visualizer";
import { useTheme } from "@/context/ThemeContext";
@@ -45,46 +45,40 @@ export function RealtimeWaveform() {
createEffect(on(numBars, (n) => viz.setBarCount(n)));
// ── Rendering ──────────────────────────────────────────────────────
const renderLine = () => {
//
// The two bar rows are CONTENT of fixed text nodes — the element tree is
// created once and only the strings change. Rebuilding the box/texts per
// bar update (30/s) tears down a renderable per frame, and the renderer
// leaks ~44KB of native memory per teardown: gigabytes over one long
// playback session.
const lines = createMemo(() => {
const bars = viz.barData();
const count = numBars();
// Loading state: the braille spinner shows while the pipeline is
// warming up — cold start (first play / after an unload), resume
// into undecoded audio, or a stalled position clock (mpv
// re-buffering after a long pause on a network stream). The store
// clears it the moment the first fresh frame renders, so stale
// bars never masquerade as live data while the pipeline re-arms.
if (viz.isLoading() || viz.isStalled()) {
return <LoadingIndicator />;
}
if (bars.length === 0) {
const placeholder = ".".repeat(count);
return (
<box flexDirection="column" gap={0}>
<text fg={theme.primary}>{placeholder}</text>
<text fg={theme.primary}>{placeholder}</text>
</box>
);
const placeholder = ".".repeat(numBars());
return { top: placeholder, bottom: placeholder };
}
const pairs = bars.map((v) => barChars(Math.floor(v * BAR_LEVELS)));
const top = pairs.map((pair) => pair.top).join("");
const bottom = pairs.map((pair) => pair.bottom).join("");
return (
<box flexDirection="column" gap={0}>
<text fg={theme.primary}>{top}</text>
<text fg={theme.primary}>{bottom}</text>
</box>
);
return {
top: pairs.map((pair) => pair.top).join(""),
bottom: pairs.map((pair) => pair.bottom).join(""),
};
});
return (
<box border borderColor={theme.border} padding={1}>
{renderLine()}
{/* Loading: braille spinner while the pipeline warms up — cold
start, resume into undecoded audio, or a stalled position
clock. The store clears it the moment the first fresh frame
renders, so stale bars never masquerade as live data. */}
<Show
when={!viz.isLoading() && !viz.isStalled()}
fallback={<LoadingIndicator />}
>
<box flexDirection="column" gap={0}>
<text fg={theme.primary}>{lines().top}</text>
<text fg={theme.primary}>{lines().bottom}</text>
</box>
</Show>
</box>
);
}