synops/docs/features/lydmixer.md
vegard b5aa5bb243 Fjern SpacetimeDB komplett (oppgave 22.4)
SpacetimeDB er nå helt fjernet fra Synops. Sanntid håndteres av
PG LISTEN/NOTIFY + WebSocket i portvokteren (maskinrommet).

Kode fjernet:
- spacetimedb/ Rust-modul og spacetime.json
- maskinrommet/src/stdb.rs (HTTP-klient for STDB-reducers)
- frontend module_bindings/ (23 auto-genererte filer)
- spacetimedb npm-avhengighet fra package.json
- scripts/test-sanntid.sh (testet STDB-flyt)

Infrastruktur:
- Docker-container stoppet og fjernet fra docker-compose.yml
- Caddy: fjernet /spacetime/* reverse proxy
- maskinrommet-env.sh: fjernet STDB_IP og SPACETIMEDB_*-variabler
- .env.example: fjernet SpacetimeDB-seksjoner

Dokumentasjon oppdatert:
- CLAUDE.md: stack, lagmodell, kjerneprinsipper, driftsmodell
- docs/arkitektur.md: skrivestien, lesestien, datalag, teknologivalg
- docs/retninger/datalaget.md: migrasjonshistorikk, status "fjernet"
- 37 andre docs oppdatert (features, concepts, infra, ops, retninger)
- Alle kode-kommentarer med STDB-referanser oppdatert

Verifisert: maskinrommet bygger og starter OK, frontend bygger OK,
helsesjekk returnerer 200. Caddy reloadet.
2026-03-18 13:39:09 +00:00

11 KiB
Raw Permalink Blame History

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 0150% GainNode (0.01.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, +612dB) BiquadFilterNode lowshelf
Exciter Harmonisk tilstedeværelse (35kHz) WaveShaperNode + highshelf
Sparkle Høyfrekvent luft (~1016kHz, +36dB) BiquadFilterNode highshelf
Monsterstemme Pitch ned 48 halvtoner AudioWorkletNode (phase vocoder)
Robotstemme Metallisk ring-modulasjon OscillatorNodeGainNode.gain

Signalflyt per kanal:

Kilde → HighPass(80Hz) → FatBottom → Exciter → Sparkle → RobotMod → 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 PG LISTEN/NOTIFY + WebSocket, slik at volumendringer, mutes, effekttogles og pad-avspilling reflekteres hos alle klienter umiddelbart.

Element Synkronisering
Volumslider PG: mixer_channels-tabell med gain-verdi per kanal
Mute PG: is_muted boolean per kanal
Effekt av/på PG: 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 maskinrommet. Ved samtidig endring av samme kanal vinner siste skriving — i praksis uproblematisk fordi endringer er visuelt synlige for alle og deltakerne koordinerer naturlig.

PG-tabell: mixer_channels med NOTIFY-trigger for sanntidspropagering via WebSocket.

API-endepunkter (maskinrommet):

  • POST /intentions/create_mixer_channel — idempotent opprettelse
  • POST /intentions/set_gain — clamped 0.01.5, viewer-sjekk
  • POST /intentions/set_mute — viewer-sjekk
  • POST /intentions/toggle_effect — JSON-toggle
  • POST /intentions/set_mixer_role — 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 <audio>-elementer. Hent MediaStreamTrack fra remote participant, opprett MediaStreamSourceNode, rut gjennom Web Audio-kjeden.
  • Utgående lyd: Mikrofon → Web Audio-kjede → MediaStreamAudioDestinationNode → publiser den prosesserte tracken via LiveKit.
  • Sound pads over nett: Send LiveKit Data Message (reliable) med pad-ID og tidsstempel. Alle klienter trigge samme pad lokalt. Akseptabel synk: <50ms.

4.3 Stemmeeffekter (AudioWorklet)

Pitch-shifting (monsterstemme) krever en AudioWorkletProcessor som kjører på lyd-tråden:

  • Algoritme: Phase vocoder med identity phase locking
  • Bibliotek: Vurder phaze eller SoundTouchJS AudioWorklet
  • Latens: ~2040ms avhengig av FFT-vindu (10242048 samples ved 48kHz)
  • Parametere: Pitch-faktor (0.5 = oktav ned, 0.7 = monster, 1.0 = normal)

Robot-stemme bruker ring-modulasjon — ingen AudioWorklet nødvendig:

  • OscillatorNode (sinus, 50200Hz) kobles til GainNode.gain AudioParam
  • Stemmekilden kobles til samme GainNode
  • Resultatet er metallisk, Dalek-aktig stemme

4.4 Pad-konfigurasjon (lagring)

Sound pads lagres i samlingsnoden sin metadata:

{
  "traits": ["recording", "mixer"],
  "mixer": {
    "pads": [
      { "label": "Jingle", "cas_hash": "abc123...", "color": "#FF6B6B" },
      { "label": "Applaus", "cas_hash": "def456...", "color": "#4ECDC4" }
    ]
  }
}

5. Avhengigheter

Avhengighet Type Lisens Formål
livekit-client npm Apache-2.0 WebRTC-klient for LiveKit
Web Audio API nettleser All lydprosessering (innebygd)
CAS eksisterende Lagring av pad-lydfiler

Ingen betalte tjenester. All lydprosessering skjer lokalt i nettleseren via Web Audio API. Stemmeeffekter (robot, monster) og EQ (fat bottom, exciter, sparkle) bygges med innebygde Web Audio-noder og en egenutviklet AudioWorkletProcessor for pitch shifting. Phase vocoder-algoritmen er veldokumentert og implementeres direkte — ingen tredjepartslisens nødvendig.

6. Node/trait-integrasjon

Lydmixeren aktiveres via mixer-traitet på en samlings-node. Krever at recording-traitet også er aktivt (LiveKit-avhengighet).

  • Trait: mixer (kategori: Lyd & video)
  • Frontend: MixerTrait-komponent i samlingssiden
  • Backend: Ingen nye endepunkter — bruker eksisterende LiveKit token-generering og CAS for pad-lydfiler

7. Avgrensning

  • Mixeren er for live-bruk, ikke postproduksjon/redigering
  • Effektene prosesseres lokalt i nettleseren — ikke på serveren
  • Hver bruker kontrollerer sin egen mixer (personlig mix)
  • Pad-synkronisering er best-effort (~50ms) — akseptabelt for lydeffekter
  • Støtter ikke multitrack-opptak (det er en separat feature)

8. Utviklingsfaser

Fase A: Grunnleggende mixer (MVP)

  • livekit-client integrasjon i frontend (koble til rom, vise deltakere)
  • Web Audio-graf: MediaStreamSource per remote track → GainNode → destination
  • Kanalstripe-UI: volumslider + mute-knapp per deltaker
  • VU-meter (AnalyserNode → canvas/CSS)
  • Master fader og master mute

Fase B: Delt mixer-kontroll

  • PG: mixer_channels-tabell + NOTIFY-trigger + maskinrommet-endepunkter
  • Frontend abonnerer på mixer-state, oppdaterer Web Audio-graf ved endringer
  • Visuell feedback: alle ser sliders bevege seg i sanntid
  • Tilgangskontroll: eier/admin kan sette deltaker til "viewer" (kun observere)

Fase C: Sound Pads

  • Pad-grid UI (4×2 grid med fargede knapper)
  • Last lydfiler fra CAS → AudioBuffer
  • Avspilling ved trykk (AudioBufferSourceNode)
  • Pad-konfigurasjon: velg lydfil, farge, label (lagres i node metadata)
  • LiveKit Data Message for synkronisert avspilling på tvers av deltakere

Fase D: Lydbehandling (EQ)

  • Fat bottom (lowshelf filter, +8dB @ 200Hz)
  • Sparkle (highshelf filter, +4dB @ 10kHz)
  • Exciter (WaveShaperNode soft-clip + highshelf @ 3.5kHz)
  • Per-kanal av/på-toggles for hver effekt (synkronisert via PG active_effects)
  • Preset-konfigurasjon: "Av", "Podcast-stemme" (bass+luft), "Radio-stemme" (bass+luft+exciter)
  • Highpass-filter (80Hz) alltid aktiv for rumble-fjerning

Fase E: Stemmeeffekter

  • Robotstemme (ring-modulasjon med OscillatorNode → GainNode.gain, frekvens 30300Hz, dybde 0100%)
  • Monsterstemme (pitch shift via egenutviklet AudioWorkletProcessor med phase vocoder, pitch 0.52.0x)
  • Effektvelger-UI per kanal (Robot/Monster-knapper med FX-seksjon, fargekodede parametersliders)
  • Parameterjustering (pitch-faktor, oscillator-frekvens, modulasjonsdybde)

9. Instruks for Claude Code

  • Lydmixeren er ren frontend — ingen nye Rust-endepunkter nødvendig
  • Bruk Web Audio API, IKKE <audio>-elementer for prosessering
  • AudioWorklet for pitch shifting — aldri ScriptProcessorNode (deprecated)
  • AudioContext må opprettes fra brukergest (klikk/tap) — nettleserkrav
  • Pad-lydfiler lagres i CAS via eksisterende upload_media intention
  • Pad-konfigurasjon i samlingsnoden sin metadata.mixer-nøkkel
  • Stemmeeffekter prosesseres lokalt — ikke send prosessert lyd over LiveKit med mindre brukeren eksplisitt vil at andre skal høre effekten