Większość opóźnienia w tłumaczeniu na żywo to czekanie: na strumień, na kolejne słowo, na zdanie. Jedyna część, na którą masz wpływ, to tryb opóźnienia: mniej zwłoki albo mniej poprawek.
Słuchacz czytający tłumaczenie na żywo widzi każdy wiersz kilka sekund po tym, jak mówca go wypowiedział. Ta luka to nie jedno opóźnienie, lecz kilka, dodających się w łańcuchu, a większość z nich to pewna forma czekania. Znajomość tego, które czekanie jest które, ułatwia wybór trybu opóźnienia i podpowiada programiście budującemu czytnik, co wyświetlać i kiedy.
Dla orientacji, zanim przejdziemy do liczb: ludzki tłumacz symultaniczny zwykle mówi z opóźnieniem dwóch do czterech sekund za mówcą. Tłumacze nazywają ten odstęp (ang. ear-voice span), a z językami o szyku czasownika na końcu radzą sobie nie przez czekanie na czasownik, lecz przez jego przewidywanie albo przez dzielenie zdania na fragmenty, które mogą stać samodzielnie. Sekunda czy dwie opóźnienia to nie wada oprogramowania; to mniej więcej tyle, ile potrafi bardzo dobry człowiek.
Każda transkrypcja na żywo w VocalStack przechodzi przez te same etapy, w tej samej kolejności, a każdy czeka na poprzedni.
Tekst w języku oryginału dociera do czytelnika niemal w chwili wypowiedzi. Tłumaczenie pojawia się sekundę lub dwie później, a wypowiedziane zdanie pojawia się po swoim tłumaczeniu, dlatego klip głosowy zawsze dociera na końcu. Pierwszy widoczny efekt całego łańcucha pojawia się jednak na długo przed głosem: sam tekst zaczyna się przesuwać.
Pierwsze, co zauważają ludzie w transkrypcji na żywo, to to, że najnowsze słowa się przesuwają. Fraza pojawia się, zmienia i w końcu się ustala. To działa etap wersji roboczej, który istnieje z powodu dotyczącego każdego rozpoznawacza mowy.
Rozpoznawacz ustala słowo, patrząc na to, co następuje po nim. „I scream” i „ice cream” brzmią tak samo, dopóki nie pojawi się kolejne słowo; końcowe „s” może należeć do tego słowa albo do następnego. Dopóki nie ma dźwięku po prawej stronie słowa, jego pisownia jest zgadywaniem, więc najnowsze słowa w każdej transkrypcji na żywo są zawsze najmniej pewne. Wersja robocza to po prostu to zgadywanie, pokazane wcześniej zamiast ukryte.
Każda wersja robocza w całości zastępuje żywy ogon, czyli słowa usłyszane od ostatniego zatwierdzenia, które wciąż się zmieniają. Zatwierdzenie jest ostateczne. Nigdy się nie zmienia, niesie czasy na poziomie słów, a zatwierdzenia są numerowane bez luk, dzięki czemu czytnik może zauważyć, czy ominął któreś.
Dla programisty to jedna reguła, której API czasu rzeczywistego wymaga: wyrenderować każdy zatwierdzony segment, a po nich najnowszą wersję roboczą, a gdy nadejdzie zatwierdzenie, wyczyścić wersję roboczą. Dopisywanie wersji roboczych zamiast ich zastępowania daje zduplikowany, migoczący tekst; zatwierdzenie po prostu pochłania ogon. Samouczek WebSocket buduje czytnik właśnie wokół tego.
Dla prowadzącego ruchomy tekst to cena niskiego opóźnienia. Pokazywanie słów, zanim fraza się skończy, sprawia, że transkrypcja wydaje się żywa. Tłumaczenie nie może jednak sobie na to pozwolić, bo słowo da się zgadnąć z połowy frazy, ale tłumaczenia nie.
Szyk wyrazów sprawia, że tłumaczenie nie może nadążyć za wersją roboczą. Czasownik w niemieckim może stać na końcu zdania, a japoński stawia czasownik na samym końcu. Każdy tłumacz potrzebuje całego zdania, by oddać je wiernie. Tłumaczenie słowo po słowie daje słuchaczowi fragmenty, które muszą zostać przeczytane ponownie, gdy zdanie się zamknie. Niemiecka mowa każe tłumaczom czekać na czasownik od dawna, zanim zrobiło to oprogramowanie.
Dlatego VocalStack tłumaczy zdania, nie segmenty. Zdania są domyślnie włączone i gdy są włączone, serwer składa zatwierdzenia w zdanie i tłumaczy je w całości. Dopóki zdanie jest otwarte, jego tłumaczenie jest tymczasowe; gdy się zamknie, tłumaczenie staje się ostateczne. Czytnik trzyma jeden wpis na zdanie i zastępuje go w miarę napływania aktualizacji.
Mowa czeka jeszcze dłużej. Odczytywane jest tylko zamknięte zdanie, bo zdanie wciąż się zmieniające byłoby syntezowane ponownie przy każdej poprawce. Długie zdanie jest odczytywane na głos w kawałkach, dzięki czemu pierwszy może zabrzmieć, zanim powstaną pozostałe.
Przy wyłączonych zdaniach każde zatwierdzenie dostaje zamiast tego własną ramkę tłumaczenia. Ta ścieżka na nic nie czeka, ale całe zdania czyta się znacznie lepiej.
Wszystkie te oczekiwania mierzy się od chwili, gdy dźwięk dociera do VocalStack. W wielu transmisjach dźwięk był już spóźniony, zanim tam dotarł.
Gdy źródłem jest strumień, a nie mikrofon, dźwięk jest już opóźniony, zanim cokolwiek zostanie transkrybowane. Pozostaje w tyle za krawędzią na żywo, czyli najnowszym momentem, jaki strumień opublikował.
Zwykły strumień audio HTTP, taki jak ten, który publikuje większość stacji radiowych w internecie, zwykle opóźnia się o sekundę lub trzy. Serwer utrzymuje bufor wstępny: kilka sekund ostatniego dźwięku, wysłanych naraz, aby odtwarzacz nowego słuchacza mógł wystartować bez zacięć. Każdy słuchacz, także VocalStack, dołącza tyle wstecz. HLS jest gorszy: jego playlisty zaczynają klienta trzy segmenty za krawędzią na żywo, co w praktyce oznacza kilka sekund do pół minuty, zależnie od długości segmentu. Standardowy DASH zwykle mieści się w tym samym zakresie co HLS; dopiero jego wariant niskiego opóźnienia schodzi wyraźnie poniżej. „Żywy” strumień stacji bywa żywy w takim samym sensie, w jakim list bywa aktualny.
Te liczby są przybliżone i dotyczą strumienia, a nie transkrypcji: żadne ustawienie po stronie VocalStack nie odzyska tych sekund. Jeśli te sekundy mają znaczenie dla wydarzenia, podawaj dźwięk do etapu z mikrofonu lub enkodera, a nie z publicznego strumienia. Opóźnienie strumienia jest ustalone, zanim VocalStack usłyszy choć jedno słowo; tryb opóźnienia to jedyne, co naprawdę można przesunąć.
Tryb opóźnienia robi jedną rzecz: ustala równowagę między opóźnieniem a dokładnością. Ustawienie znajduje się w sekcji Advanced podczas tworzenia transkrypcji na żywo, a na karcie Speed w trakcie jej trwania. Tam znacznik pokazuje AUTO, SPEED lub ACCURACY (Fast pojawia się jako SPEED). W API session.update zmienia tryb w trakcie strumienia.
Kompromis jest taki, jak sugerują nazwy. Fast szybciej wypuszcza tekst, ale poprawia go częściej; Accuracy trochę dłużej czeka i poprawia rzadziej. Siedząca publiczność czytająca przetłumaczone napisy rzadko zauważa dodatkową sekundę, ale zauważa słowa, które wciąż się zmieniają. Dlatego Accuracy bywa łagodniejszym wyborem, o ile wykres opóźnienia pozostaje płaski. Auto pokazuje, co robi: wiersz, który przyspieszył, aby zamknąć lukę, ma w transkrypcji prowadzącego odznakę FAST MODE z informacją, że dokładność może tam być niższa.
Bez względu na tryb transkrypcja musi być gotowa przed pierwszym słowem, a jej przygotowanie to jedyne opóźnienie, które łańcuch płaci tylko raz.
W niektórych planach transkrypcja na żywo potrzebuje kilku sekund na start. Czas zależy od planu: wiersz Start-up speed na stronie z cennikiem obejmuje od zimnego startu trwającego do 15 sekund w planie Pay as you go aż po stałą gotowość w planie Enterprise.
W API czekanie widać jako ramki session.loading podczas startu sesji, a potem session.ready; klient wysyła dźwięk dopiero po tym. Słuchacz, który w międzyczasie otworzy link, widzi: „Na żywo. Pierwszy wiersz pojawi się za chwilę.” Gdy transkrypcja działa, to opóźnienie zostaje za tobą, a jedyne pytanie, jakie pozostaje, to ile z pozostałego opóźnienia dźwigasz.
Podczas działania transkrypcji na żywo karta Speed u prowadzącego pokazuje, czym to wszystko się składa: bieżący odstęp między mówcą a tekstem, obok znacznik trybu, oraz wykres tego odstępu w trakcie nagrania ze średnią i szczytem. W samej transkrypcji wiersz, który dotarł do słuchaczy późno, ma odznakę z liczbą sekund między chwilą wypowiedzi a chwilą dotarcia.
Wykres, który pozostaje nisko i płasko, oznacza, że tryb pasuje do dźwięku. Rosnący wykres oznacza, że potok zostaje w tyle. W trybie Accuracy może to być moment, by przejść na Auto, który wymienia część dokładności na szybkość, aż odstęp się zmniejszy.
Programiści dostają zamiast tego jedną liczbę. Gdy sesja czasu rzeczywistego się kończy, session.done zawiera stats.realtimeFactor, czyli czas transkrybowanego dźwięku podzielony przez czas poświęcony na jego transkrypcję. Uwaga dotycząca konwencji: w literaturze o rozpoznawaniu mowy współczynnik czasu rzeczywistego oznacza zwykle odwrotność, czyli czas przetwarzania do czasu dźwięku, gdzie niżej znaczy lepiej. VocalStack podaje go odwrotnie, więc tutaj wyżej znaczy lepiej: sesja na żywo jest blisko 1, a wartość poniżej 1 oznacza, że dźwięk dotarł szybciej, niż potok zdołał go transkrybować.
Żadna z tych sekund nie jest tajemnicza. Część należy do strumienia, część do słowa czekającego na kolejne słowo, część do zdania, którego potrzebuje tłumaczenie, a reszta do wybranego trybu opóźnienia. Dokumentacja API zawiera szczegóły ramka po ramce, a demo na stronie głównej pokazuje cały łańcuch w działaniu.