Steven
Steven5 min. skaitymo

Ausinukų klaida: kai pašnekovas dingdavo iš mūsų transkripcijos

Dvikanalė transkripcija nepriekaištingai veikė su nešiojamojo kompiuterio garsiakalbiais ir su ausis apgaubiančiomis ausinėmis. Paskui kažkas įsikišo ausinukus — ir žmogus, su kuriuo jis kalbėjo, tiesiog dingdavo iš transkripcijos, nes Chromium tyliai nutildydavo mūsų garsą tą pačią akimirką, kai pašnekovas prabildavo. Vienuolika taisymų žlugo. Dvyliktasis pakeitė visą architektūrą.

Inžinerija
Garsas
Transkripcija
GeekBye leidimai
Ausinukų klaida: kai pašnekovas dingdavo iš mūsų transkripcijos

GeekBye transkribuoja dvi pokalbio puses: tavo mikrofoną viename kanale, pašnekovo garsą (tai, ką groja tavo kompiuteris) kitame. Tai veikė nepriekaištingai su nešiojamojo kompiuterio garsiakalbiais. Veikė su ausis apgaubiančiomis ausinėmis. Paskui naudotojas įsikišo ausinukus — ir žmogus, su kuriuo jis kalbėjo, dingdavo iš transkripcijos. Ne iškraipytas. Ne priskirtas ne tam. Dingęs.

Tai istorija apie GeekBye v1.6.12, klaidą, kuriai prireikė vienuolikos žlugusių taisymų, ir akimirką, kai supratome, kad kovojame su naršykle, o ne dirbame kartu su ja.

Takelis, kuris buvo gyvas ir tuščias

Simptomas buvo šiurpokas. Pradedi susitikimą, viskas atrodo gerai, ištari vieną žodį — ir nuo tos akimirkos pašnekovo garso kanalas nuslysta į gryną skaitmeninę tylą. Mes tai išmatavome: sistemos garso signalas sėdėjo normaliame energijos lygyje (RMS apie 0.03–0.06), o tą akimirką, kai mikrofonas pagaudavo tavo balsą, jis nukrisdavo iki 0.0000 ir likdavo ten iki sesijos pabaigos.

Štai dalis, kuri kainavo mums dienas. Kiekvienas naršyklės API tvirtino, kad garso takelis yra visiškai sveikas. readyState: live. enabled: true. muted: false. Pagal kiekvieną būsenos vėliavėlę, kurią platforma atskleisdavo, srautas buvo tvarkoje. Negyvi buvo pavyzdžiai — gyvas vamzdis, nešantis vien nulius. Ilgai netikėjome savo pačių energijos matuokliu, nes oficiali versija tvirtino, kad takelis veikia.

Trigeris buvo būdingas būtent ausinukams, o priežastis fizinė: ausinukas yra vienas įrenginys, kuris tuo pat metu yra ir tavo mikrofono įvestis, ir garso išvestis. Nešiojamojo kompiuterio garsiakalbiai ir ausis apgaubiančios ausinės tai atskiria — išvestis eina į vieną vietą, mikrofonas yra kitur. Bet kai įvestis ir išvestis yra tas pats įrenginys, tu pagal apibrėžimą turi grįžtamojo ryšio kelią. Mūsų tvirta išvada (tai gyvena naršyklės viduriuose, tad negalime pacituoti skyriaus ir eilutės) yra ta, kad Chromium aptinka šią mikrofono-plius-garsiakalbio-viename-įrenginyje formą, nusprendžia, jog tai grįžtamojo ryšio kilpa, tuoj sucypsianti, ir apsauginiai nutildo fiksavimą — tyliai, nepranešdamas programai jokia vėliavėle, kurią programa galėtų perskaityti.

Vienuolika taisymų, keli iš jų blogesni

Iš pradžių puolėme tai spręsti akivaizdžiais būdais ir kiekvieną bandymą užsirašėme. Išjungti aido slopinimą, triukšmo slopinimą ir automatinį stiprinimą abiejuose srautuose. Paleisti abu srautus nuosekliai su delsa, o ne kartu. Duoti kiekvienam srautui savo diskretizavimo dažnį. Pridėti tylų stiprinimo mazgą. Nukreipti viską per vieną bendrą garso kontekstą. Pereiti prie AudioWorklet. Nusileisti iki pat žalio WebCodecs takelio procesoriaus.

Vienuolika požiūrių. Jie ne tik žlugo — keli padarė blogiau. Vienas įnešė 30 sekundžių vėlavimą. Kitas privertė tavo mikrofono transkripciją pasirodyti pažymėtą kaip pašnekovas. Buvome giliai brūzgynuose, gydėme kiekvieną simptomą, o klaida vis slinkdavo.

Proveržis buvo permąstymas: kiekvienas ligšiolinis taisymas bandė išlaikyti du atskirus srautus sveikus. Bet du atskiri srautai ir buvo būtent tai, ką Chromium grįžtamojo ryšio euristika galėjo pamatyti ir nutildyti. O kas, jei nebūtų ką lyginti?

Taisymas: duok naršyklei vieną srautą, ne du

v1.6.12 liovėsi kovojęs ir pakeitė architektūrą. Užuot siuntęs mikrofoną ir sistemos garsą kaip du nepriklausomus srautus, jis sulieja juos į vieną mono srautą kliento pusėje, dar prieš kam nors paliekant programą. Kai abu šaltiniai teka per vieną konvejerį, takeliai negali numirti nepriklausomai — nebėra dviejų įrenginių grįžtamojo ryšio formos, kurią naršyklė galėtų aptikti ir nutildyti. Trigerio sąlyga tiesiog nustoja egzistuoti.

Buvo ir maloni premija. Du srautai reiškė du WebSocket ryšius klientas-į-backend ir du ryšius backend-į-tiekėją; vienas sulietas srautas suglaudė tai iki vieno ir vieno. Ausinukų klaidos taisymas perpus sumažino mūsų transkripcijos kaštą kaip šalutinį poveikį.

Bet mono suliejimas turi akivaizdžią problemą: jei suplaki abu žmones į vieną kanalą, kaip vis dar žinai, kas ką pasakė? Išmetei būtent tą atskyrimą, kuris tau tai sakė.

„Kas kalbėjo" perstatymas iš energijos

Atsakymas buvo išmatuoti kiekvieno šaltinio garsumą prieš juos suliejant ir prie kiekvieno garso gabalo pridėti užuominą, kuris šaltinis dominavo — mikrofonas, sistema ar tyla. Garsesnis šaltinis laimi. Pakankamai paprasta su garsiakalbiais.

Ausinukams prireikė dar vieno sluoksnio, ir čia aidas grįžta į istoriją — nukreiptas priešingai, nei atspėtum. Su ausinukais pašnekovo balsas iš ausinuko nuteka atgal į mikrofoną. Tad be atsargumo jo aidas, ateinantis į tavo mikrofono kanalą, priskiriamas tau. Taisymas yra aidą suvokiančių slenksčių rinkinys: kai sistemos garsas groja virš mažo lygio, kartelė, kurią tavo mikrofonas turi peršokti, kad būtų įskaityta kaip „tu kalbi", pašoka aukštai į viršų — nuo įprasto 0.015 iki 0.1. Kitaip tariant: kol pašnekovas kalba, tavo mikrofonas turi būti aiškiai, vienareikšmiškai garsesnis už jo aidą, kol žodžius priskirsime tau. Dviprasmiški kadrai pagal nutylėjimą eina į „sistemą" darant prielaidą, kad tai aidas. Tai sąmoningas nukrypimas, dėl kurio priskyrimas tampa teisingas būtent toje situacijoje, kuri anksčiau jį klaidindavo.

Šis proof-of-concept — mono suliejimas plius energija paremtas, aidą suvokiantis balso aptikimas — yra priskyrimo sistemos, su kuria GeekBye veikia šiandien, protėvis. Pastoliai buvo išrauti tame pačiame leidime, vos tik pasiteisino, o energijos matematika vėliau sukietėjo į kaip reikiant išbandytą modulį. Bet v1.6.12 yra ta vieta, kur idėja gimė, po spaudimu, naudotojo su pora ausinukų dėka.

Trys dalykai, kurių išmokė ausinukų klaida

  1. Sveika rankena gali nešti negyvus duomenis. Visas tyrimas rėmėsi pasitikėjimu mūsų energijos matuokliu labiau nei platformos būsenos vėliavėlėmis — live, enabled, unmuted, visos jos melavo, kol pavyzdžiai buvo nuliai. Stebėk turinį, ne API savipranešimą. Jei srautas teigia esąs tvarkoje, išmatuok, ar iš tikrųjų taip.
  2. Kai platforma kovoja su tavo architektūra, keisk architektūrą — ne platformą. Negalėjome perkalbėti Chromium grįžtamojo ryšio apsaugos, ir kiekvienas bandymas išjungti ją pakraščiais žlugo arba atsimušė rikošetu. Pašalinus stebimą sąlygą — du srautai, tampantys vienu — visa problema išnyko. Beveik visada pigiau pašalinti trigerį, nei laimėti kovą su naršykle.
  3. Kai suglaudi kanalus dėl tvirtumo, privalai perstatyti tai, ką sunaikinai. Suliejimas į mono nužudė kanalų atskyrimą, kuris nešė „kas kalbėjo". Tą informaciją teko išvesti iš naujo iš prieš suliejimą buvusios energijos ir aidą suvokiančių slenksčių. Kompensuojanti logika nėra priedas prie taisymo — ji yra pusė taisymo.

Tai ketvirtas skyrius patikimumo istorijos, kuri tampa GeekBye v2. Ankstesnį skyrių rasi čia: nutrūkęs ryšys neturėtų nulaužti visos tavo programos (v1.6.8); konvejerio perrašymą, ant kurio buvo pastatytas šis garso darbas, ištrynėme 5000 eilučių garso kodo (v1.6.0); o visą lanką — programinės įrangos tobulybės pristatymo anatomiją.