revert: remove level meters and VU meter feature
Reverts: - fix: move VU meter above volume controls to stop blocking mute button - Merge feature/graph-ux-meters (region fix, toggle, segmented VU meters) - Merge feature/level-meters (pw_stream peak metering, /api/peaks) VU meters / level meters don't belong in a patchbay. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy } from 'svelte';
|
||||
import {
|
||||
nodes, ports, links, connected, patchbay, peaks,
|
||||
nodes, ports, links, connected, patchbay,
|
||||
portById,
|
||||
initGraph, destroyGraph,
|
||||
connectPorts, disconnectPorts,
|
||||
@@ -139,22 +139,6 @@
|
||||
return $patchbay.aliases?.[nd.name] || nd.nick || nd.name;
|
||||
}
|
||||
|
||||
// Graph visibility toggle
|
||||
let showGraph = $state(true);
|
||||
|
||||
// Level meter: returns how many of `total` segments should be lit
|
||||
function meterSegs(nodeId: number, total: number): number {
|
||||
const p = $peaks[nodeId] ?? 0;
|
||||
if (p <= 0) return 0;
|
||||
const db = Math.max(-60, 20 * Math.log10(p));
|
||||
return Math.round(Math.max(0, (db + 60) / 60) * total);
|
||||
}
|
||||
// Segment color by position (0 = leftmost/quietest)
|
||||
function segColor(i: number, total: number): string {
|
||||
const pct = i / total;
|
||||
return pct >= 0.90 ? '#c44' : pct >= 0.75 ? '#ca4' : '#4a9';
|
||||
}
|
||||
|
||||
// Build computed layout
|
||||
let graphNodes = $derived.by(() => {
|
||||
const n = $nodes;
|
||||
@@ -210,7 +194,7 @@
|
||||
const headerH = 22;
|
||||
const portH = 16;
|
||||
const maxPorts = Math.max(allInPorts.length, allOutPorts.length, 1);
|
||||
const height = headerH + maxPorts * portH + 36; // 20 for volume slider + 16 for VU meter
|
||||
const height = headerH + maxPorts * portH + 20; // extra 20 for volume slider
|
||||
const width = 220;
|
||||
|
||||
const portPositions = new Map<number, { x: number; y: number }>();
|
||||
@@ -510,8 +494,6 @@
|
||||
</label>
|
||||
|
||||
<span class="stats">{$nodes.length}N {$ports.length}P {$links.length}L {#if $patchbay.pinned_connections.length > 0}{$patchbay.pinned_connections.length}p{/if}</span>
|
||||
<span class="sep"></span>
|
||||
<button class="toggle" class:active={showGraph} onclick={() => showGraph = !showGraph} title="Show/hide graph canvas">Graph</button>
|
||||
</div>
|
||||
|
||||
<!-- Virtual device dropdown -->
|
||||
@@ -556,7 +538,6 @@
|
||||
{/if}
|
||||
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
{#if showGraph}
|
||||
<svg
|
||||
bind:this={svgEl}
|
||||
viewBox="{viewBox.x} {viewBox.y} {viewBox.w} {viewBox.h}"
|
||||
@@ -696,22 +677,9 @@
|
||||
{Math.round(Math.max(0, Math.min(1, nd.volume)) * 100)}%
|
||||
</text>
|
||||
|
||||
<!-- VU meter: 20 segmented LEDs (above the volume/mute controls) -->
|
||||
{#each Array(20) as _, i}
|
||||
<rect
|
||||
x={nd.x + 8 + i * ((nd.width - 16) / 20)}
|
||||
y={nd.y + nd.height - 35}
|
||||
width={(nd.width - 16) / 20 - 1.5}
|
||||
height="10"
|
||||
rx="1"
|
||||
fill={i < meterSegs(nd.id, 20) ? segColor(i, 20) : '#1e1e28'}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
</g>
|
||||
{/each}
|
||||
</svg>
|
||||
{/if}
|
||||
|
||||
<!-- Context menu -->
|
||||
{#if contextMenu}
|
||||
@@ -923,11 +891,11 @@
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.wrap { width: 100%; height: 100vh; background: #14141e; display: flex; flex-direction: column; position: relative; overflow: hidden; user-select: none; -webkit-user-select: none; }
|
||||
.canvas { flex: 1; min-height: 0; width: 100%; display: block; cursor: default; z-index: 1; user-select: none; -webkit-user-select: none; }
|
||||
.wrap { width: 100%; height: 100vh; background: #14141e; position: relative; overflow: hidden; user-select: none; -webkit-user-select: none; }
|
||||
.canvas { width: 100%; height: 100%; display: block; cursor: default; position: absolute; top: 0; left: 0; z-index: 1; user-select: none; -webkit-user-select: none; }
|
||||
|
||||
.toolbar {
|
||||
position: relative; flex-shrink: 0; z-index: 10;
|
||||
position: absolute; top: 0; left: 0; right: 0; z-index: 10;
|
||||
display: flex; align-items: center; gap: 6px;
|
||||
padding: 4px 10px;
|
||||
background: rgba(16, 16, 24, 0.97);
|
||||
|
||||
@@ -8,21 +8,6 @@ export const ports = writable<Port[]>([]);
|
||||
export const links = writable<Link[]>([]);
|
||||
export const connected = writable(false);
|
||||
|
||||
// Level meter peaks: node_id → linear peak (0–1)
|
||||
export const peaks = writable<Record<number, number>>({});
|
||||
let peakInterval: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
async function pollPeaks() {
|
||||
try {
|
||||
const res = await fetch('/api/peaks');
|
||||
if (!res.ok) return;
|
||||
const raw: Record<string, number> = await res.json();
|
||||
const out: Record<number, number> = {};
|
||||
for (const [k, v] of Object.entries(raw)) out[Number(k)] = v;
|
||||
peaks.set(out);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// Patchbay state
|
||||
export const patchbay = writable<PatchbayState>({
|
||||
profiles: {},
|
||||
@@ -117,8 +102,6 @@ export async function initGraph() {
|
||||
unsubscribe = subscribe((graph: GraphMessage) => {
|
||||
applyGraph(graph);
|
||||
});
|
||||
|
||||
peakInterval = setInterval(pollPeaks, 100);
|
||||
}
|
||||
|
||||
export function destroyGraph() {
|
||||
@@ -127,11 +110,6 @@ export function destroyGraph() {
|
||||
unsubscribe = null;
|
||||
connected.set(false);
|
||||
}
|
||||
if (peakInterval) {
|
||||
clearInterval(peakInterval);
|
||||
peakInterval = null;
|
||||
}
|
||||
peaks.set({});
|
||||
}
|
||||
|
||||
// Patchbay operations
|
||||
|
||||
Reference in New Issue
Block a user