Süni İntellekt Dublyajı Niyə Sinxrondan Çıxır — və Niyə Bu Qaçılmaz Deyil
Avtomatik dublyajlar üçüncü dəqiqədə sinxronu itirir. Səbəb tərcümə və ya səs keyfiyyəti deyil, yığım dövründəki bir sətir riyaziyyatdır. Sürüşmənin haradan gəldiyini, işləməyən üç aşkar həlli və kursoru toplamaq əvəzinə mənbə timeline-dan təyin etməyin bütün arxitekturanı niyə dəyişdiyini real koda baxaraq izah edirəm.
Sınadığım hər avtomatik dublyaj aləti eyni cür uğursuz olurdu, həmişə də eyni nöqtədə.
İlk otuz saniyə qüsursuz keçir. Tərcümə olunmuş səs danışanın üstünə oturur, ritm təbii görünür və problemin həll olunduğuna inanmağa başlayırsan. İki dəqiqədən sonra nəsə sürüşür. Səs, ağız dayandıqdan bir an sonra bitir. Üçüncü dəqiqədə dublyaj artıq tam bir cümlə geridədir və illüziya birdəfəlik dağılır. Səsin şəkillə mübahisə etdiyi videonu heç kim axıra qədər izləmir.
Bu, keyfiyyət problemi deyil. Transkripsiya qüsursuz, tərcümə əla ola bilər, hər cümlə ayrılıqda gözəl səslənə bilər. Dublyaj yenə də sürüşür. Nasazlıq struktur xarakterlidir, bir sətir riyaziyyatın içindədir və bir dəfə gördükdən sonra artıq görməzdən gələ bilmirsən.
Problemi yaradan sətir
Dublyajı yığmağın sadəlövh yolu ən aşkar yoldur. Başlanğıc vaxtları olan subtitr seqmentlərin var. Hər cümləni sintez edirsən, sonra da onları bir-birinin ardınca düzürsən:
cursor = 0
for segment in segments:
audio = synthesize(segment.text)
timeline.place(audio, at=cursor)
cursor += audio.duration # ← here
cursor += audio.duration. Vəssalam. Baq budur.
Sintez olunmuş nitq heç vaxt əvəz etdiyi mənbə cümləsi ilə eyni uzunluqda olmur. Alman tərcüməsi ingiliscədən uzun çıxır, yığcam tərcümə isə qısa. TTS mühərriki öz payını da əlavə edir. Hər sətir bir neçə yüz millisaniyə yayınır, o qədər az ki, gözə dəymir.
Amma += bu xətaları toplayır. İki yüz seqment, hər birində orta hesabla 300 ms artıq. Sonda altmış saniyə sürüşmə. İstənilən nöqtədəki xəta ondan əvvəlki bütün xətaların cəmidir. Yəni dublyaj tədricən pisləşmir. Monoton şəkildə uzaqlaşır və video nə qədər uzundursa, nəticə bir o qədər pisdir.
Dublyaj demolarının həmişə qısa olmasının səbəbi budur.
İşləməyən üç həll
Sürüşməni görəndən sonra üç həll özü təklif olunur. Üçünü də sınadım.
Səsi yavaşlat ki, yerinə sığsın. Klip uzundursa, pəncərəni genişləndirmək əvəzinə səsin özünü dartmaq. Bu dərhal uğursuz olur: yavaşladılmış nitq sürünmüş səslənir. Saitlər yayılır, samitlər yumşalır və dinləyici səbəbini deyə bilməmişdən çox əvvəl nəyinsə səhv olduğunu hiss edir. Nitqi sürətləndirmək dözüləndir, tez danışanları hər gün eşidirik, amma heç kim ləng çəkilişdə danışmır.
Sükutu kəs. Millisaniyələri geri qazanmaq üçün pauzaları qırxmaq. Ancaq pauzalar boşluq deyil. Danışan orada nəfəs alır, cümlə orada yerini tapır. Onları çıxaranda dublyaj hərraca çevrilir. Sinxron problemini çatdırma problemi ilə dəyişmiş olursan.
Videonu yenidən vaxtla. Şəkli səsə uyğunlaşdırmaq üçün dartmaq. Bu işləyir və video montajçısı da elə bunu edərdi, amma indi sən mətn-səs mühərrikinə uyğunlaşmaq üçün videonu yenidən kodlaşdırırsan. Quyruq çox iri bir iti tərpədir.
Üçünün də altında eyni səhv fərziyyə var: sürüşmənin haradasa udulmalı olan audio problemi olduğu. Elə deyil. Bu, mühasibat problemidir.
Kursoru toplama, təyin et
Mənbə video hər cümlənin nə vaxt başlamalı olduğunu artıq bilir. Bu məlumat transkriptdədir, dəqiqdir, sadəlövh dövr isə onu atıb keçir. Mövqeyi dublyaj etdiyi timeline-dan deyil, indicə yaratdığı səsdən götürür.
Onda götürmə. Hər cümləyə öz mənbə başlanğıcından növbəti cümlənin mənbə başlanğıcına qədər uzanan slot ver və səsi o slota yerləşdir:
def _plan_anchored_block(actual_ms, start_ms, next_start_ms):
"""Returns (trim_ms, pad_ms) for the slot [start_ms, next_start_ms]."""
room = next_start_ms - start_ms
if room > 0 and actual_ms > room:
return (actual_ms - room), 0.0 # over-runs: trim it
return 0.0, max(0.0, room - actual_ms) # short: pad the remainder
Slotundan kənara çıxan klip qırxılır (qısa fade ilə, yoxsa klik səsi verir). Qısa gələn klipin qalan yeri sükutla doldurulur. Sonra əsas məqam: növbəti cümlə üçün kursor ümumiyyətlə hesablanmır.
return float(next_start_ms)
O, mənbə timeline-dan təyin olunur. Hər cümlə orijinal danışanın başladığı yerdə başlayır, ondan əvvəlki cümləyə nə olmasından asılı olmayaraq. Bir seqmentdəki xəta həmin seqmentdə qalır. Sürüşmə azaldılmır, kompensasiya da edilmir. Sadəcə yığılmağa yeri yoxdur.
Bütün ideya budur. Dörd sətir riyaziyyat, üçüncü dəqiqədə dağılan dublyajla dağılmayan dublyaj arasındakı fərq.
Bu qərarın sənə qiyməti
Gözləmədiyim hissə budur: anchored yerləşdirməni seçmək təkcə vaxtlamanı düzəltmir. Qalan arxitekturanı da diktə edir, çünki indi hər cümlənin sərt son müddəti var.
Tərcüməçiyə uzunluq büdcəsi lazımdır. Tərcümə öz slotundan iki dəfə uzundursa, anchoring onu qırxmaq deməkdir, sən də söz itirirsən. Deməli uzunluq məhdudiyyəti yuxarıya, tərcümə mərhələsinə keçməlidir: hər sətrə öz müddətindən və danışıq sürətindən çıxarılan simvol büdcəsi verilir, model isə hər ikisi mümkün olmayanda hərfi yox, yığcam olmağa yönəldilir. Vaxtlamanı düzəltmək sonda prompt mühəndisliyi məsələsinə çevrilir.
Nitq yalnız sürətlənə bilər, yavaşlaya yox. Qısa kliplər dartılmaq əvəzinə doldurulduğu üçün time-stretcher-in cəmi bir işi qalır:
if not allow_slowdown:
if ratio < 1.0:
return 1.0 # shorter than the slot: keep natural pace
return min(ratio, STRETCH_MAX_SPEEDUP)
Qısa klip öz təbii tempini saxlayır, artıq qalan vaxt isə sükuta çevrilir — pauza onsuz da belə səslənir. Yalnız kənara çıxanlar sıxılır, o da müəyyən həddə qədər, çünki bir yerdən sonra sürətli danışıq özü ayrıca qüsura çevrilir.
Ekspressivliyi məhdudlaşdırmaq lazımdır. Bu, sonradan sancır. Operatora "yavaş, dramatik çatdırma" istəməyə icazə versən və bunu birbaşa instruksiya qəbul edən TTS mühərrikinə ötürsən, daha uzun səs alırsan, yerləşdirmə qatı isə onu qırxır. Üslub, üstünə qoyulduğu sinxronu səssizcə dağıdır. Həll, tempi danışıqsız qəbul etməkdir: istifadəçi hansı üslubu istəsə də, vaxtlama göstərişi ən sonda əlavə olunur, çünki modellər sonuncu təlimata ən çox çəki verir. Emosiya cümlənin necə səsləndiyini dəyişir, nə qədər çəkdiyini isə heç vaxt.
Kursor haqqında bir qərar tərcüməyə, sintezə və istifadəçiyə təhlükəsiz verə biləcəyin yaradıcı nəzarətin həcminə yayılır. Onu baq düzəlişi yox, arxitektura qərarı edən də budur.
Bunun işlədiyini haradan bilirsən
Dublyajın "sinxronda qalır" iddiası asan və yoxlanılmazdır, ona görə necə yoxlayacağını demək lazımdır. Qulaqla dinləmək məni müəyyən yerə qədər apardı. İki şey daha çox kömək etdi.
Birincisi golden regression harness: pipeline-ın deterministik yarısını (cümlə birləşdirmə, uzunluq büdcələri, SRT timestamp-ları, yerləşdirmə riyaziyyatı) məlum girişlə qeyd edirsən və sonrakı hər işləməni onunla diff edirsən. Model yoxdur, şəbəkə yoxdur, API açarı yoxdur, ona görə CI-da saniyələr ərzində işləyir. Vaxtlama riyaziyyatı məhz belə kilidlənə bilsin deyə saf saxlanılıb.
O birisi round-trip keyfiyyət qapısı: sintez olunmuş hər klipi ikinci ASR modeli ilə geri transkripsiya edirsən və deyilməli olan mətnlə tutuşdurursan. Çıxan word error rate kobud alətdir, amma dinləməklə tutulmayan şeyləri tutur. Mənim halımda, geniş istifadə olunan bir bulud TTS mühərrikinin bəzi az resurslu dilləri elə korladığını göstərdi ki, daha kiçik yerli səs onu açıq şəkildə üstələyirdi.
Bu ölçmənin yanında bir dərs də gəldi. Dil üzrə WER rəqəmi dərc etdim, sonra başqa mənbə materialda yenidən işlədəndə eyni mühərrik iki dəfə pis nəticə göstərdi. Hər iki işləmə düzgün idi. Sadəcə rəqəmin üzərinə daşıya biləcəyindən artıq yük qoyulmuşdu. Mənbə klipi yanında olmayan WER rəqəmi müdafiə edə biləcəyin iddia deyil. Bunu öz benchmark-ın haqqında öyrənmək xoşagəlməz şeydir və ölçmənin özünü yox, ölçən skripti paylaşmaq üçün yaxşı səbəbdir.
Kod
Bunların hamısı Voxa-dadır: MIT lisenziyalı, tək fayllıq Python dublyaj mühərriki. Transkripsiya edir, kontekst və uzunluq büdcəsi ilə tərcümə edir, dörd mühərrikdən biri ilə danışır və nəticəni orijinalın üstünə miksləyir:
pipx install voxa-dub
voxa talk.mp4 --target_lang ru
Defolt ayarlar üçün API açarı lazım deyil. Yerləşdirmə kodu təxminən qırx sətirdir və yuxarıdakı riyaziyyat məqalə üçün sadələşdirilmiş variant deyil, işin özüdür.
Kursorun yaratdığı fərq belə görünür. Eyni üç dəqiqəlik klip bir dəfə dublyaj olunub, iki cür yerləşdirilib, sayğac isə real seqment vaxtları ilə işləyir: sadəlövh yığım danışandan on səkkiz saniyə geri qalır, anchored yerləşdirmə isə birinci cümlədən sonuncuya qədər sıfırda durur.
Bu sahədə nəsə qurursansa: yığım dövrünün kursorla nə etdiyini yoxla. Əgər toplayırsa, bu baq səndə var və ilk uzun videonun təxminən üçüncü dəqiqəsində onu tapacaqsan.
Tez-tez verilən suallar
Bu problem yalnız süni intellekt dublyajına aiddir?
Xeyr. Sintez olunmuş audio parçalarını ardıcıl birləşdirən istənilən pipeline eyni riski daşıyır, o cümlədən sadə mətn-səs oxuyucuları və özün yazdığın skript. Peşəkar dublyaj studiyalarında problem yaranmır, çünki orada hər replikanı montajçı şəklə baxaraq yerləşdirir. Yəni məsələ modelin keyfiyyətində yox, kursorun necə hesablanmasındadır.
Anchored yerləşdirmə sözlərin itməsinə səbəb olmur?
Tərcümə öz slotundan uzun çıxarsa, bəli, klipin sonu qırxılır. Elə buna görə uzunluq məhdudiyyəti tərcümə mərhələsinə köçürülür: hər sətrə öz müddətindən çıxarılan simvol büdcəsi verilir və model hərfi yox, yığcam tərcüməyə yönəldilir. Qırxma son çarədir, ilk müdafiə xətti deyil.
Sürüşməni hazır dublyaj faylında sonradan düzəltmək olar?
Praktikada yox. Xəta yığıla-yığıla böyüdüyü üçün onu geri açmaq hər seqmentin mənbə vaxtlarını tələb edir. O vaxtlar əlindədirsə, yerləşdirməni əvvəldən anchored etmək daha ucuz başa gəlir. Düzəliş yığım dövründədir, post-prosesdə deyil.
Aqşin Miranov
Founder & CEO Servoogle-da • 14 avqust 2026
Bu mövzu sizin layihənizə uyğun gəlir?
Layihənizi danışaq. Birbaşa mühəndislərlə işləyirsiniz, satış şöbəsi ilə deyil.
NDA ilə qorunan müzakirə • 48 saat ərzində cavab • Öhdəliksiz
