Files
WhispAssist/src/lib/components/LevelMeter.svelte
T
2026-07-12 12:53:26 -05:00

83 lines
2.1 KiB
Svelte

<script lang="ts">
// Live input level meter while recording (T7.3, FR-CAP-5/7). One bar with the
// system/loopback level (green) and — when the mic is enabled — the microphone
// level overlaid in the accent colour, so both sides of the call are visible
// at a glance. Each stream shows an rms fill + a peak marker.
import { t } from "../i18n/index.svelte";
let {
rms,
peak,
micRms = 0,
micPeak = 0,
showMic = false,
}: {
rms: number;
peak: number;
micRms?: number;
micPeak?: number;
showMic?: boolean;
} = $props();
// Perceptual loudness isn't linear; sqrt gives a meter that "looks right"
// for typical speech levels instead of sitting near-empty most of the time.
const pct = (v: number) => Math.min(1, Math.sqrt(Math.max(0, v))) * 100;
let rmsPct = $derived(pct(rms));
let peakPct = $derived(pct(peak));
let micRmsPct = $derived(pct(micRms));
let micPeakPct = $derived(pct(micPeak));
</script>
<div
class="meter"
role="meter"
aria-label={showMic ? t("levelmeter.system_mic") : t("levelmeter.system")}
aria-valuenow={Math.round(Math.max(rmsPct, showMic ? micRmsPct : 0))}
aria-valuemin={0}
aria-valuemax={100}
>
<div class="fill system" style="width: {rmsPct}%"></div>
<div class="peak system" style="left: {peakPct}%"></div>
{#if showMic}
<div class="fill mic" style="width: {micRmsPct}%"></div>
<div class="peak mic" style="left: {micPeakPct}%"></div>
{/if}
</div>
<style>
.meter {
position: relative;
width: 60px;
height: 8px;
border-radius: var(--radius-full);
background: var(--border);
overflow: hidden;
}
.fill {
position: absolute;
inset: 0 auto 0 0;
transition: width 60ms linear;
/* Overlap is visible because the mic layer is translucent. */
opacity: 0.7;
}
.fill.system {
background: var(--success);
}
.fill.mic {
background: var(--accent);
}
.peak {
position: absolute;
top: 0;
bottom: 0;
width: 2px;
transition: left 60ms linear;
}
.peak.system {
background: var(--fg);
opacity: 0.6;
}
.peak.mic {
background: var(--accent);
}
</style>