feat(microham-eq): speechnorm Auto-Level + überarbeitete EQ-Oberfläche

- Adaptive Pegel-Normalisierung (speechnorm) in die TX-Audio-EQ-Kette
  aufgenommen: gleicher Sendepegel aus jedem Browser/Mikrofon, unabhängig
  von Client-AGC und Mic-Pegel. Neue Sektion "Pegel-Normalisierung" mit
  Ziel-Pegel (%) und Max. Anhebung (dB); build/parse round-trip getestet.
- speechnorm sitzt nach dem Noise Gate und vor dem Limiter, damit
  Grundrauschen in Pausen zuerst gegatet und nicht hochgezogen wird.
- EQ-Admin-UI überarbeitet: gruppierte Sektionen, Preset-Chips (inkl.
  neuem "Konstanter Pegel"), Switch-Toggles, Wert-Badges, Accent-Slider,
  Live-Vorschau der FFmpeg-Kette; abhängige Regler werden deaktiviert,
  wenn ihre Sektion aus ist.
- Stimmigkeit korrigiert: De-Esser-Frequenz liegt jetzt im SSB-Durchlass-
  band (war 3500–8000 Hz, oberhalb des Lowpass), Lowpass auf 3200 Hz
  begrenzt, agate mit explizitem attack; Gate-Parser tolerant gemacht.
- Presets (flat/level/dx/ragchew) auf sinnvolle SSB-Werte neu abgestimmt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ARCG Admin (Claude)
2026-10-25 21:34:32 +01:00
parent 1acbb569dd
commit 5ac966572a
2 changed files with 617 additions and 234 deletions

View File

@@ -762,24 +762,298 @@ button:disabled {
.plugin-eq-builder {
border: 1px solid var(--surface-border);
border-radius: 10px;
padding: 0.65rem;
border-radius: 16px;
padding: 1rem;
display: grid;
gap: 0.5rem;
background: rgba(255, 255, 255, 0.02);
gap: 0.9rem;
background:
radial-gradient(120% 140% at 0% 0%, color-mix(in srgb, var(--primary) 10%, transparent), transparent 55%),
color-mix(in srgb, var(--surface) 70%, transparent);
box-shadow: inset 0 1px 0 color-mix(in srgb, #fff 6%, transparent);
}
.plugin-eq-row {
display: grid;
grid-template-columns: 130px 1fr 88px;
gap: 0.5rem;
align-items: center;
.eq-head {
display: flex;
align-items: flex-start;
gap: 0.6rem;
}
.plugin-eq-checkbox {
.eq-head-text {
display: grid;
gap: 0.15rem;
}
.eq-head-title {
font-family: var(--font-display);
font-size: 1.05rem;
letter-spacing: 0.01em;
}
.eq-head-hint {
color: var(--muted);
}
.eq-presets {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
.eq-chip {
appearance: none;
border: 1px solid var(--surface-border);
background: color-mix(in srgb, var(--surface) 55%, transparent);
color: var(--text);
border-radius: 999px;
padding: 0.35rem 0.85rem;
font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
transition: border-color var(--transition), background var(--transition), transform var(--transition), box-shadow var(--transition);
}
.eq-chip:hover {
transform: translateY(-1px);
border-color: color-mix(in srgb, var(--primary) 55%, var(--surface-border));
}
.eq-chip.is-star::before {
content: "★ ";
color: var(--primary);
}
.eq-chip.is-active {
background: var(--primary);
color: var(--primary-ink);
border-color: var(--primary);
box-shadow: 0 6px 18px color-mix(in srgb, var(--primary) 32%, transparent);
}
.eq-preset-info {
color: var(--muted);
min-height: 1.1rem;
}
.eq-section {
border: 1px solid var(--surface-border);
border-radius: 12px;
padding: 0.75rem 0.85rem;
display: grid;
gap: 0.55rem;
background: color-mix(in srgb, var(--surface) 45%, transparent);
transition: border-color var(--transition), box-shadow var(--transition);
}
.eq-section.is-active {
border-color: color-mix(in srgb, var(--primary) 45%, var(--surface-border));
box-shadow: 0 0 0 1px color-mix(in srgb, var(--primary) 18%, transparent) inset;
}
.eq-section-head {
display: flex;
align-items: center;
gap: 0.5rem;
justify-content: space-between;
gap: 0.6rem;
}
.eq-section-title {
margin: 0;
font-family: var(--font-display);
font-size: 0.95rem;
font-weight: 700;
letter-spacing: 0.02em;
text-transform: uppercase;
color: var(--text);
}
.eq-section-sub {
margin: 0;
font-size: 0.82rem;
color: var(--muted);
}
.eq-section-body {
display: grid;
gap: 0.6rem;
}
.eq-note {
margin: 0.1rem 0 0;
font-size: 0.8rem;
color: var(--muted);
padding: 0.5rem 0.6rem;
border-radius: 8px;
background: color-mix(in srgb, var(--primary) 8%, transparent);
border: 1px solid color-mix(in srgb, var(--primary) 18%, transparent);
}
.eq-control {
display: grid;
gap: 0.3rem;
transition: opacity var(--transition);
}
.eq-control.is-disabled {
opacity: 0.4;
pointer-events: none;
}
.eq-control-head {
display: flex;
align-items: baseline;
gap: 0.4rem;
}
.eq-control-label {
flex: 1;
font-size: 0.86rem;
color: var(--text);
}
.eq-control-value {
width: 72px;
text-align: right;
background: color-mix(in srgb, var(--surface) 70%, transparent);
border: 1px solid var(--surface-border);
border-radius: 8px;
color: var(--text);
padding: 0.2rem 0.4rem;
font-variant-numeric: tabular-nums;
font-weight: 600;
}
.eq-control-unit {
width: 2.2rem;
font-size: 0.78rem;
color: var(--muted);
}
.eq-range {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 6px;
border-radius: 999px;
background: color-mix(in srgb, var(--muted) 35%, transparent);
outline: none;
cursor: pointer;
}
.eq-range::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--primary);
border: 2px solid var(--primary-ink);
box-shadow: 0 2px 8px color-mix(in srgb, var(--primary) 45%, transparent);
transition: transform var(--transition);
}
.eq-range::-webkit-slider-thumb:hover {
transform: scale(1.12);
}
.eq-range::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
background: var(--primary);
border: 2px solid var(--primary-ink);
}
.eq-switch {
display: inline-flex;
align-items: center;
gap: 0.55rem;
cursor: pointer;
user-select: none;
}
.eq-switch input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.eq-switch-track {
position: relative;
flex: 0 0 auto;
width: 40px;
height: 22px;
border-radius: 999px;
background: color-mix(in srgb, var(--muted) 40%, transparent);
border: 1px solid var(--surface-border);
transition: background var(--transition);
}
.eq-switch-track::after {
content: "";
position: absolute;
top: 2px;
left: 2px;
width: 16px;
height: 16px;
border-radius: 50%;
background: #fff;
transition: transform var(--transition);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.35);
}
.eq-switch input:checked + .eq-switch-track {
background: var(--primary);
border-color: var(--primary);
}
.eq-switch input:checked + .eq-switch-track::after {
transform: translateX(18px);
}
.eq-switch input:focus-visible + .eq-switch-track {
box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 35%, transparent);
}
.eq-switch-label {
font-size: 0.86rem;
color: var(--text);
}
.eq-sub-toggle {
padding-top: 0.15rem;
}
.eq-preview {
display: grid;
gap: 0.3rem;
}
.eq-preview-label {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--muted);
}
.eq-preview-code {
display: block;
font-family: "SFMono-Regular", "Consolas", "Liberation Mono", monospace;
font-size: 0.78rem;
line-height: 1.5;
color: var(--text);
background: color-mix(in srgb, var(--bg-0) 55%, transparent);
border: 1px solid var(--surface-border);
border-radius: 10px;
padding: 0.6rem 0.7rem;
overflow-wrap: anywhere;
white-space: pre-wrap;
}
@media (max-width: 540px) {
.eq-control-head {
flex-wrap: wrap;
}
}
dt {