Ga naar inhoud

Vergaderingen en transcriptie

Control Center kan een gesprek opnemen en je een net verslag teruggeven — een samenvatting, actiepunten en beslissingen — waarbij spraakherkenning en sprekersscheiding op je eigen hardware draaien. Denk aan Granola, maar dan self-hosted en verweven met dezelfde werkruimte waarin je agenten leven.

Een vergadering is een opgenomen, getranscribeerde sessie. Dat is niet hetzelfde als een agenda-afspraak: een afspraak is een geplande verplichting die uit Google wordt gesynchroniseerd, terwijl een vergadering iets is dat je daadwerkelijk hebt opgenomen. De twee kunnen gekoppeld worden, want je kunt vanuit een afspraak een opname starten, maar het blijven afzonderlijke records.

Elke vergadering is werkruimtegebonden, net als alles in Control Center. Vergaderingen die in de ene werkruimte zijn opgenomen, duiken nooit op in een andere.

“On-device” is het juiste instinct maar het verkeerde woord. De client vangt audio op en streamt 16 kHz PCM16-frames naar de machine waarop cc_server draait. Die host doet al het overige: hij schrijft de WAV’s per kanaal onder <dataDir>/meetings/<meetingId>/, transcribeert in een worker-isolate, filtert echo en diariseert achteraf. Op een standalone desktop is die host dezelfde machine. Wijs je naar een externe server, dan is dat niet zo — dus “er verlaat niets mijn laptop” geldt alleen als de server je laptop is.

Er is nog één uitzondering, en dat is de belangrijkste.

Alles vóór de samenvatting — opname, transcriptie, echo-onderdrukking, diarisatie, matching van stemafdrukken — draait op de server zonder netwerkverkeer.

De modellen voor embedding, diarisatie en spraak (ASR) worden bij de eerste boot force-geïnstalleerd (overgeslagen bij een demo), zodat sprekersscheiding en opnemen werken zonder dat je naar de instellingen hoeft. ASR is nog steeds selecteerbaar — het is enkele honderden megabytes groot, en er worden vier builds meegeleverd in de keuzelijst. Spraak vereist na die eerste download nog één herstart, omdat de transcriber zijn model bepaalt voordat de download klaar kan zijn.

De vier opties in de keuzelijst, met verschillen in grootte, taaldekking en nauwkeurigheid:

  • Parakeet TDT 0.6B v3 — meertalig, de aanbevolen standaard (~600 MB)
  • Parakeet TDT 0.6B v2 — Engels (~480 MB)
  • Whisper large-v3-turbo — meertalig (~626 MB)
  • Whisper base.en — alleen Engels, de kleine historische standaard (~198 MB)

Modellen zijn de enige artefacten die de server tijdens runtime ophaalt; elke native bibliotheek wordt meegeleverd in de bundel.

Een vergadering neemt twee audiokanalen tegelijk op:

  • Jij (“me”): je microfoon.
  • Zij (“them”): de audio-uitvoer van de vergadering, opgevangen via een loopback zonder driver — Core Audio process/device-taps op macOS, WASAPI-loopback op Windows, een PipeWire-/PulseAudio-monitor op Linux.

Omdat de twee kanalen apart worden opgevangen, is het transcript vanaf het begin aan sprekers toegewezen: jouw woorden krijgen het label me, iedereen in het gesprek krijgt het label them.

Je eigen stem lekt vanuit de systeemuitvoer terug naar de microfoon en zou anders twee keer worden getranscribeerd. Twee filters voorkomen dat. AEC3 op signaalniveau draait bij elke opname in externe modus en is vereist — als de native AEC-bibliotheek niet kan laden, weigert de opname te starten in plaats van stil een met echo vervuilde microfoon te streamen. (De persoonlijke modus heeft geen referentie aan de andere kant om tegen te onderdrukken, dus daar laat AEC de audio ongewijzigd door.) Daarbovenop draait op elk platform een tekstfilter voor echo aan de hostzijde.

Alle microfoonverwerking op OS-niveau is bewust uitgeschakeld. Het inschakelen van de eigen echo-onderdrukking of automatische versterking van het platform op macOS schakelt de microfoon over op Voice Processing I/O, wat in deze build zowel de microfoon als de systeemaudiotap om zeep hielp.

Terwijl je opneemt, wordt audio in rollende vensters gedecodeerd — afgekapt bij een korte stilte aan het einde, of op een maximale vensterlengte — buiten de UI-thread. Elk venster wordt een aan een spreker gekoppeld transcriptsegment met offsets in milliseconden.

Vensters die nooit boven een RMS-drempel uitkwamen, worden overgeslagen zonder te worden gedecodeerd, omdat een spraakmodel stilte weergeeft als gehallucineerde non-speech-tokens en het decoderen verspilde CPU is. De praktische consequentie: een aanhoudend zeer stille spreker levert geen transcript op. Komt iemand met niets terug, controleer dan het ingangsniveau voordat je het model de schuld geeft.

Nadat je stopt, draait diarisatie offline over de opname en splitst het externe kanaal op in afzonderlijke sprekers (Person 1, Person 2, …) die je kunt hernoemen. Het aantal sprekers wordt afgeleid uit een clusterdrempel, niet uit een aantal dat je zelf instelt — dus het is een drempelartefact en een stille of overlappende spreker kan bij een buur worden samengevoegd. Het transcript wordt weergegeven als regels [mm:ss] SPEAKER: text.

Een spreker hernoemen doet meer dan één transcript van een label voorzien. Control Center kan de stemafdruk van de spreker opslaan als een stemprofiel, zodat het die persoon in toekomstige vergaderingen automatisch herkent. Wanneer je een gediariseerde spreker een naam geeft, wordt gevraagd of je de stemafdruk wilt opslaan; doe je dat, dan wordt die verwerkt in een doorlopend centroid voor die naam en met nieuwe sprekers vergeleken op cosinusgelijkenis, zodat dezelfde persoon de volgende keer met zijn naam opduikt zonder dat je hem opnieuw labelt.

Stemprofielen zijn werkruimtegebonden en overschrijden de grens nooit. Beheer ze via Instellingen → Werkruimte → Vergaderingen. Een spreker hernoemen die eerder werd opgeslagen, schrijft de oude naam uit en biedt aan de nieuwe naam in te schrijven; een profiel verwijderen haalt de opgeslagen stemafdruk weg, maar laat namen die al op eerdere vergaderingen zijn toegepast intact.

Wanneer een opname stopt, publiceert de server een MeetingRecordingStopped domeingebeurtenis. Het ingebouwde pipeline-sjabloon meeting_summary wordt door die gebeurtenis getriggerd. De recorder wacht er niet op; de vergadering doorloopt simpelweg zijn statuslevenscyclus:

recording → processing → done

De samenvattingsagent ontvangt de titel, je ruwe live-notities en het transcript, en geeft gestructureerde JSON terug. Welke secties hij schrijft, wordt bepaald door het actieve sjabloon voor vergadernotities van de werkruimte (Instellingen → Werkruimte → Vergaderingen); het actieve sjabloon geldt voor nieuwe en opnieuw uitgevoerde samenvattingen. De persist-stappen van de pipeline schrijven die JSON vervolgens naar afzonderlijke rijen:

  • enhancedNotes en summary → de notities van de vergadering
  • elk actiepunt → een rij MeetingActionItem (inhoud, eigenaar, optionele ticketkoppeling)
  • elke beslissing → een rij MeetingDecision

Actiepunten en beslissingen worden nooit uit vrije markdown gehaald, alleen uit de gestructureerde arrays van de agent. Als een run geen gestructureerde uitvoer oplevert, worden de persist-stappen overgeslagen en blijft het ruwe transcript als fallback bewaard, zodat je het record nooit kwijtraakt. Niets in de pipeline markeert de vergadering als done — dat doet één enkele reconciler op basis van de terminale gebeurtenis van de run, zodat een andere stap die faalt geen halfgeschreven vergadering kan achterlaten die al klaar lijkt.

Oppervlak Wat het toont
/meetings De lijst met vergaderingen, met aantallen actiepunten en beslissingen
/meetings/record De live-opname-HUD: je notities aan de ene kant, het streamende transcript aan de andere
/meetings/:meetingId De details van een vergadering: tabbladen Notities, Transcript, Actiepunten en Beslissingen
Instellingen → Werkruimte → Vergaderingen Sjablonen voor vergadernotities en opgeslagen stemprofielen voor deze werkruimte

Opnemen gebeurt zonder driver op alle drie de desktopplatforms (Core Audio-taps op macOS, WASAPI op Windows, PipeWire op Linux).

De webclient neemt ook op, wat de rest van deze pagina met zijn desktopbril kan verbergen: de browser krijgt je microfoon via getUserMedia en de vergaderaudio via getDisplayMedia (schermdeling), verlaagt naar 16 kHz PCM16 en streamt naar dezelfde host. Daar gelden drie kanttekeningen:

  • Wat je deelt moet een audiotrack bevatten. Safari, Firefox en schermdeling op volledig scherm in macOS leveren er geen en de opname weigert te starten.
  • Er is geen native AEC3. De eigen echo-onderdrukking van de browser wordt op de microfoon aangevraagd; het tekstuele echofilter aan de hostzijde draait nog steeds. De HUD toont een ingangsniveaumeter die wordt aangestuurd door de PCM van de microfoon.
  • Opnieuw samenvatten en verwerking annuleren zijn alleen op de desktop beschikbaar.

De actie Opnemen en koppelen in de agenda is alleen op de desktop beschikbaar en meldt dat op het web ronduit, ook al werkt de opnameflow van het vergaderingscherm zelf daar wel.