diff --git a/docs/features/lydmixer.md b/docs/features/lydmixer.md new file mode 100644 index 0000000..54d0556 --- /dev/null +++ b/docs/features/lydmixer.md @@ -0,0 +1,249 @@ +# Feature: Lydmixer (Virtuelt studioverktøy) +**Filsti:** `docs/features/lydmixer.md` + +## 1. Konsept +En nettleserbasert lydmixer inspirert av RødeCaster Pro II. Gir programledere +og møtedeltakere kontroll over lydnivåer, lydeffekter og stemmeeffekter +direkte i Synops — uten behov for ekstern maskinvare. + +Bygget på Web Audio API med LiveKit-integrasjon. Alle lydstrømmer (mikrofon, +remote-deltakere, sound pads) rutes gjennom en Web Audio-graf som gir +per-kanal prosessering før avspilling. + +## 2. Brukeropplevelse +1. Brukeren åpner studioet/møterommet og kobler seg til LiveKit-rommet. +2. I bunnen av skjermen vises en **mixerstripe** med én kanal per deltaker. +3. Hver kanal har: volumslider, mute-knapp, og valgfri effektkjede. +4. Over mixeren ligger et **pad-brett** med konfigurerbare lydeffekter. +5. En **effektvelger** per kanal lar brukeren slå av/på stemmeeffekter + (monster, robot) og lydbehandling (fat bottom, exciter, sparkle). + +## 3. Kjernekomponenter + +### 3.1 Kanalstripe (per deltaker) +Hver LiveKit-deltaker får en dedikert kanalstripe i mixeren: + +| Element | Funksjon | Web Audio | +|---|---|---| +| **Volumslider** | Visuell fader 0–150% | `GainNode` (0.0–1.5) | +| **Nød-mute** | Stor, rød knapp — umiddelbar demping | `gain.setValueAtTime(0, now)` | +| **Nivåmeter** | VU-meter som viser live lydnivå | `AnalyserNode` → canvas | +| **Navnelabel** | Deltakerens display_name | Fra LiveKit participant | + +### 3.2 Effektkjede (per kanal, valgfri) +Hver kanal kan ha en kjede av prosesseringsmoduler som slås av/på individuelt: + +| Effekt | Beskrivelse | Web Audio | +|---|---|---| +| **Fat bottom** | Lavfrekvent fylde (~200Hz, +6–12dB) | `BiquadFilterNode` lowshelf | +| **Exciter** | Harmonisk tilstedeværelse (3–5kHz) | `WaveShaperNode` + highshelf | +| **Sparkle** | Høyfrekvent luft (~10–16kHz, +3–6dB) | `BiquadFilterNode` highshelf | +| **Monsterstemme** | Pitch ned 4–8 halvtoner | `AudioWorkletNode` (phase vocoder) | +| **Robotstemme** | Metallisk ring-modulasjon | `OscillatorNode` → `GainNode.gain` | + +Signalflyt per kanal: +``` +Kilde → HighPass(80Hz) → FatBottom → Exciter → Sparkle → PitchShift → GainNode(fader) → Master +``` + +### 3.3 Sound Pads +Et grid med konfigurerbare lyd-pads (inspirert av RødeCaster Pro II sine 8×8 pads): + +| Egenskap | Detalj | +|---|---| +| **Layout** | Grid, f.eks. 4×2 (utvidbart) | +| **Avspilling** | Trykk → spill fra start til slutt | +| **Lydkilde** | Forhåndslastede `AudioBuffer` fra CAS | +| **Volum** | Egen `GainNode` per pad | +| **Synkronisering** | LiveKit Data Message → alle klienter spiller samtidig | +| **Konfigurasjon** | Velg lydfil, farge, label per pad | + +Eksempler på standard-pads: jingle/intro, applaus, latter, dramatisk pause, +"breaking news"-sting, rim shot, sad trombone, airhorn. + +### 3.4 Delt mixer-kontroll (flerbruker) +Alle deltakere i rommet kan se og bruke mixeren samtidig. Mixer-state +synkroniseres i sanntid via SpacetimeDB, slik at volumendringer, mutes, +effekttogles og pad-avspilling reflekteres hos alle klienter umiddelbart. + +| Element | Synkronisering | +|---|---| +| **Volumslider** | STDB: `MixerChannel`-tabell med `gain`-verdi per kanal | +| **Mute** | STDB: `is_muted` boolean per kanal | +| **Effekt av/på** | STDB: `active_effects` JSON per kanal | +| **Pad-trigger** | LiveKit Data Message (lav latens) | +| **Pad-konfig** | Node metadata (persistent, sjelden endring) | + +**Tilgangsnivåer:** +- **Full kontroll** (default): alle deltakere kan justere alle kanaler, + trigge pads og endre effekter. Tilsvarer at alle sitter rundt samme + RødeCaster. +- **Begrenset**: eier/admin kan låse enkeltdeltakere til kun "viewer" — + de ser mixeren live men kan ikke interagere. Bruker eksisterende + rolle-system (owner/admin/member-edges). + +**Konflikthåndtering:** Last-write-wins. Volumslidere er kontinuerlige +verdier som oppdateres via STDB-reducers. Ved samtidig endring av samme +kanal vinner siste skriving — i praksis uproblematisk fordi endringer +er visuelt synlige for alle og deltakerne koordinerer naturlig. + +**SpacetimeDB-tabeller:** + +```rust +#[spacetimedb::table(accessor = mixer_channel, public)] +pub struct MixerChannel { + #[primary_key] + pub id: String, // "{room_id}:{target_user_id}" + pub room_id: String, // "communication_{node_uuid}" + pub target_user_id: String, // hvem kanalen tilhører + pub gain: f64, // 0.0–1.5 + pub is_muted: bool, + pub active_effects: String, // JSON: {"fat_bottom": true, "robot": false, ...} + pub role: String, // "editor" | "viewer" — tilgangskontroll per kanal + pub updated_by: String, // hvem som sist endret + pub updated_at: Timestamp, +} +``` + +**STDB Reducers:** +- `create_mixer_channel(room_id, target_user_id, updated_by)` — idempotent opprettelse +- `set_gain(room_id, target_user_id, gain, updated_by)` — clamped 0.0–1.5, viewer-sjekk +- `set_mute(room_id, target_user_id, is_muted, updated_by)` — viewer-sjekk +- `toggle_effect(room_id, target_user_id, effect_name, updated_by)` — JSON-toggle +- `delete_mixer_channel(room_id, target_user_id)` — opprydding ved disconnect +- `set_mixer_role(room_id, target_user_id, role, updated_by)` — sett editor/viewer + +Mixer-kanaler ryddes automatisk ved `close_live_room` og `clear_all`. + +### 3.5 Master-seksjon +| Element | Funksjon | +|---|---| +| **Master fader** | Samlet utgangsnivå | +| **Master mute** | Demper all lyd | +| **Master VU** | Stereo nivåmeter for utgangen | + +## 4. Teknisk arkitektur + +### 4.1 Web Audio-graf +``` + ┌─────────────────────────────────┐ + │ AudioContext │ + │ │ + Remote Track 1 ──→ MediaStreamSource → EffektKjede → GainNode ─┐ + Remote Track 2 ──→ MediaStreamSource → EffektKjede → GainNode ─┤ + Lokal mikrofon ──→ MediaStreamSource → EffektKjede → GainNode ─┼→ MasterGain → destination + Sound Pad ──→ AudioBufferSource ─────────────→ GainNode ─┘ + │ │ + └─────────────────────────────────┘ +``` + +### 4.2 LiveKit-integrasjon +- **Innkommende lyd:** Deaktiver LiveKit sin auto-attach av `