
Øreproppfeilen: da den andre personen forsvant fra transkripsjonen vår
Tokanals transkripsjon fungerte feilfritt på laptop-høyttalere og over-øret-hodetelefoner. Så plugget noen inn ørepropper, og personen de snakket med forsvant rett og slett fra transkripsjonen — fordi Chromium stille dempet lyden vår i samme øyeblikk de begynte å snakke. Elleve rettelser mislyktes. Den tolvte endret hele arkitekturen.
GeekBye transkriberer to sider av en samtale: mikrofonen din på én kanal, den andre deltakerens lyd (det datamaskinen din spiller av) på en annen. Det fungerte feilfritt på laptop-høyttalere. Det fungerte på over-øret-hodetelefoner. Så plugget en bruker inn ørepropper, og personen de snakket med forsvant fra transkripsjonen. Ikke forvrengt. Ikke feiltilskrevet. Borte.
Dette er historien om GeekBye v1.6.12, feilen som tok elleve mislykkede rettelser, og øyeblikket da vi innså at vi slåss mot nettleseren i stedet for å jobbe sammen med den.
Sporet som var levende og tomt
Symptomet var nifst. Du startet et møte, alt så bra ut, du sa ett ord — og fra det øyeblikket gikk den andre personens lydkanal til ren digital stillhet. Vi målte det: systemlyd-signalet lå på et normalt energinivå (en RMS rundt 0.03–0.06), og i det øyeblikket mikrofonen fanget stemmen din, falt det til 0.0000 og ble der resten av økten.
Her er delen som kostet oss dager. Hvert nettleser-API insisterte på at lydsporet var fullstendig friskt. readyState: live. enabled: true. muted: false. Etter hvert statusflagg plattformen eksponerte var strømmen fin. Det var samplene som var døde — et levende rør som ikke bar annet enn nuller. Vi brukte lang tid på ikke å tro på vår egen energimåler, fordi den offisielle historien sa at sporet fungerte.
Utløseren var spesifikk for ørepropper, og årsaken er fysisk: en ørepropp er én enhet som samtidig er mikrofoninngangen din og lydutgangen din. Laptop-høyttalere og over-øret-hodetelefoner skiller disse — utgangen går ett sted, mikrofonen er et annet sted. Men når inngang og utgang er samme enhet, har du per definisjon en tilbakekoblingsvei. Vår sterke slutning (dette bor i nettleserens innvoller, så vi kan ikke sitere kapittel og vers) er at Chromium oppdager denne mikrofon-pluss-høyttaler-på-én-enhet-formen, avgjør at det er en tilbakekoblingssløyfe i ferd med å hyle, og beskyttende demper opptaket — stille, uten å fortelle appen det gjennom noen av flaggene en app kan lese.
Elleve rettelser, flere av dem verre
Vi angrep det på de opplagte måtene først, og skrev ned hvert forsøk. Slå av ekkokansellering, støyreduksjon og autoforsterkning på begge strømmene. Starte de to strømmene sekvensielt med en forsinkelse i stedet for sammen. Gi hver strøm sin egen samplingsfrekvens. Legge til en stille forsterkningsnode. Rute alt gjennom én delt lydkontekst. Gå over til en AudioWorklet. Falle helt ned til den rå WebCodecs-sporprosessoren.
Elleve tilnærminger. De mislyktes ikke bare — flere gjorde det verre. En introduserte en 30-sekunders forsinkelse. En annen fikk mikrofonens transkript til å dukke opp merket som den andre personen. Vi var dypt inne i krattet, behandlet hvert symptom, og feilen fortsatte å flytte seg.
Gjennombruddet var en omformulering: hver rettelse så langt forsøkte å holde to separate strømmer friske. Men to separate strømmer var nettopp det som Chromiums tilbakekoblingsheuristikk kunne se og dempe. Hva om det ikke fantes noe å sammenligne?
Rettelsen: gi nettleseren én strøm, ikke to
v1.6.12 sluttet å slåss og endret arkitekturen. I stedet for å sende mikrofonen og systemlyden som to uavhengige strømmer mikser den dem til én mono-strøm på klienten før noe forlater appen. Når begge kildene flyter gjennom én pipeline, kan ikke sporene dø uavhengig av hverandre — det finnes ikke lenger noen to-enhets-tilbakekoblingsform for nettleseren å oppdage og dempe. Utløsningsbetingelsen slutter rett og slett å eksistere.
Det var en herlig bonus. To strømmer betydde to klient-til-backend-WebSockets og to backend-til-leverandør-tilkoblinger; én mikset strøm kollapset det til én og én. Å rette øreproppfeilen halverte transkripsjonskostnaden vår som en sidegevinst.
Men mono-miksing har et opplagt problem: hvis du blander begge personene til én kanal, hvordan vet du fortsatt hvem som sa hva? Du har jo kastet bort selve separasjonen som fortalte deg det.
Gjenoppbygge "hvem som snakket" fra energi
Svaret var å måle lydstyrken til hver kilde før de ble mikset, og feste et hint til hver lydbit om hvilken kilde som dominerte — mikrofon, system eller stillhet. Høyere kilde vinner. Enkelt nok på høyttalere.
Ørepropper trengte enda et lag, og det er her ekkoet kommer tilbake i historien — rettet motsatt vei av hva du ville gjette. På ørepropper lekker den andre personens stemme ut av proppen og tilbake inn i mikrofonen. Så uten omtanke blir deres ekko, som ankommer på mikrofonkanalen din, tilskrevet deg. Rettelsen er et sett ekkobevisste terskler: når systemlyden spilles over et lite gulv, hopper listen mikrofonen din må klare for å telle som "du snakker" kraftig opp — fra en normal 0.015 til 0.1. Med andre ord: mens den andre personen snakker, må mikrofonen din være tydelig, utvetydig høyere enn ekkoet deres før vi krediterer ordene til deg. Tvetydige rammer havner som standard på "system", ut fra antakelsen om at de er ekko. Det er en bevisst slagside som får tilskrivingen riktig i akkurat den situasjonen som før fikk den feil.
Dette konseptbeviset — mono-miksing pluss energibasert, ekkobevisst stemmedeteksjon — er stamfaren til tilskrivingssystemet GeekBye kjører i dag. Stillaset ble revet ut i samme utgivelse så snart det hadde bevist seg, og energimatematikken herdet senere til en skikkelig testet modul. Men v1.6.12 er der ideen ble født — under press, av en bruker med et par ørepropper.
Tre ting øreproppfeilen lærte oss
- Et friskt håndtak kan bære død data. Hele undersøkelsen dreide seg om å stole på energimåleren vår framfor plattformens statusflagg —
live,enabled,unmuted, alle løy mens samplene var nuller. Overvåk innholdet, ikke API-ets egenrapport. Hvis en strøm påstår at den er fin, mål om den faktisk er det. - Når plattformen slåss mot arkitekturen din, endre arkitekturen — ikke plattformen. Vi kunne ikke argumentere Chromiums tilbakekoblingsbeskyttelse i hjel, og hvert forsøk på å slå den av i kantene mislyktes eller slo tilbake. Å fjerne den observerbare betingelsen — to strømmer blir til én — fikk hele problemet til å forsvinne. Det er nesten alltid billigere å fjerne utløseren enn å vinne en kamp med nettleseren.
- Når du slår sammen kanaler for robusthet, må du gjenoppbygge det du ødela. Å mikse til mono drepte kanalseparasjonen som bar "hvem som snakket". Den informasjonen måtte utledes på nytt fra energi før miksing og ekkobevisste terskler. Den kompenserende logikken er ikke en ettertanke til rettelsen — den er halvparten av rettelsen.
Dette er det fjerde kapittelet i pålitelighetshistorien som blir GeekBye v2. For det forrige kapittelet, se en tapt tilkobling burde ikke krasje hele appen din (v1.6.8); for pipeline-omskrivingen dette lydarbeidet bygde på, vi slettet 5 000 linjer med lydkode (v1.6.0); og for hele buen, anatomien av å skipe programvare til perfeksjon.