فاز 6 · درس 11

پردازش بلادرنگ صدا در دستیارهای صوتی هوش مصنوعی

نوع
ساختنی
زمان
حدود 75 دقیقه
زبان‌ها
Python
پیش‌نیاز
فاز 6 · درس 02 (اسپکتروگرام‌ها)، فاز 6 · درس 04 (بازشناسی گفتار یا ASR)، فاز 6 · درس 07 (سنتز گفتار یا TTS)
یک موج صوتی درخشان با حلقه‌ای از بلوک‌های داده و گیت‌های کنترل تاخیر روی زمینه‌ای تاریک و مینیمال
فهرست مطالب
  1. صورت مسئله
  2. مفاهیم پایه
  3. خطاهای رایج
  4. پیاده‌سازی از صفر
  5. مرحله‌ی 1: بافر حلقوی
  6. مرحله‌ی 2: گیت VAD
  7. مرحله‌ی 3: ASR استریمینگ
  8. مرحله‌ی 4: مدیریت وقفه و پریدن در صحبت
  9. استفاده از ابزارهای واقعی
  10. دام‌های رایج
  11. خروجی این درس
  12. تمرین‌ها
  13. اصطلاحات کلیدی
  14. برای مطالعه‌ی بیشتر

در سیستم‌های پردازش صوتی آفلاین، می‌توانید یک فایل ضبط‌شده را بدون دغدغه‌ی زمان به مدل بدهید و متن تحویل بگیرید؛ اما در دستیارهای مکالمه‌ای و بات‌های تماس تلفنی، تاخیر بالای 500 میلی‌ثانیه حس تعامل طبیعی را نابود می‌کند. اگر پاسخی فراتر از 1.5 ثانیه طول بکشد، کاربر تصور می‌کند دستیار متوقف شده یا صدای او را نشنیده است.

رسیدن به تاخیر ایده‌آل حدود 400 میلی‌ثانیه برای چرخه‌ی کامل شنیدن، پردازش در مدل زبانی و پخش پاسخ صوتی، نیازمند معماری استریمینگ دقیق، بافرهای حلقوی (Ring Buffer)، تشخیص فوری فعالیت صوتی (VAD) و مکانیزم قطع آنی کلام (Barge-in) است. این درس پایه‌های مهندسی پردازش استریمینگ صدا را برای دستیارهای تعاملی مدرن باز می‌کند.

اگر برای بخش هوش مصنوعی و مدل‌های زبانی قصد استفاده از APIهای خارجی مثل OpenAI Realtime API را دارید، توجه داشته باشید که کارت‌های بانکی ایرانی برای پرداخت مستقیم ارزی قابل استفاده نیستند و برخی ارائه‌دهندگان ابری نیز دسترسی از IPهای داخلی را محدود کرده‌اند. با این حال، هسته‌ی اصلی این پایپ‌لاین‌ها مانند بافرینگ و VAD روی CPU محلی اجرا می‌شوند و می‌توانید زیرساخت انتقال صدا را با ابزارهای متن‌باز پایه‌ریزی کنید.

پایپ‌لاین‌های دسته‌ای (Batch) یک فایل را پردازش می‌کنند. پایپ‌لاین‌های بلادرنگ (Real-time)، 20 میلی‌ثانیه‌ی بعدی را پیش از رسیدن 20 میلی‌ثانیه‌ی پس از آن پردازش می‌کنند. تمام هوش‌های مصنوعی مکالمه‌ای، استودیوهای پخش زنده و بات‌های تلفنی با همین سقف تاخیر (Latency budget) زنده می‌مانند یا می‌میرند.

صورت مسئله

می‌خواهید دستیار صوتی‌تان حس زنده بودن را منتقل کند. تاخیر جابه‌جایی نوبت صحبت میان انسان‌ها (فاصله‌ی سکوت تا شروع پاسخ) حدود 230 میلی‌ثانیه است. هر تاخیری بالاتر از 500 میلی‌ثانیه حس رباتیک ایجاد می‌کند و فراتر از 1500 میلی‌ثانیه کاملاً خراب به نظر می‌رسد. سقف تاخیر مجاز برای یک چرخه‌ی کامل شنیدن ← فهمیدن ← پاسخ دادن ← گفتن در سال 2026 به این صورت است:

مرحله سقف تاخیر
میکروفون ← بافر 20 میلی‌ثانیه
VAD 10 میلی‌ثانیه
ASR استریمینگ 150 میلی‌ثانیه
مدل زبانی بزرگ (اولین توکن) 100 میلی‌ثانیه
TTS استریمینگ (اولین چانک) 100 میلی‌ثانیه
رندر ← اسپیکر 20 میلی‌ثانیه
مجموع ~400 میلی‌ثانیه

مدل Moshi (تیم Kyutai، سال 2024) تاخیر دوطرفه‌ی کامل (Full-duplex) را به 200 میلی‌ثانیه رساند. مدل GPT-4o-realtime (سال 2024) تاخیری حدود 320 میلی‌ثانیه ثبت کرده است. پایپ‌لاین‌های آبشاری در سال 2022 با تاخیر 2500 میلی‌ثانیه‌ای عرضه می‌شدند. این بهبود 10 برابری حاصل سه تکنیک است: (1) استریمینگ در تمام بخش‌ها، (2) پایپ‌لاین‌بندی ناهمگام با استفاده از نتایج جزئی، و (3) تولید قابل توقف و قطع‌شدن.

مفاهیم پایه

پایپ‌لاین استریمینگ صوتی با بافر حلقوی، گیت VAD و قابلیت وقفه

فریم / چانک / پنجره (Frame / chunk / window). صدای بلادرنگ در قالب بلوک‌هایی با اندازه‌ی ثابت جریان پیدا می‌کند. یک انتخاب رایج: 20 میلی‌ثانیه (320 نمونه در نرخ نمونه‌برداری 16 کیلوهرتز). تمام بخش‌های بعدی باید بتوانند پا به پای این ریتم پیش بروند.

بافر حلقوی (Ring buffer). یک بافر چرخشی با اندازه‌ی ثابت است. تردِ تولیدکننده (Producer thread) فریم‌های جدید را می‌نویسد و تردِ مصرف‌کننده (Consumer thread) آن‌ها را می‌خواند. این ساختار مانع از تخصیص حافظه در مسیر بحرانی و پرمصرف (Hot path) می‌شود. اندازه ≈ سقف تاخیر × نرخ نمونه‌برداری؛ بنابراین یک بافر حلقوی 2 ثانیه‌ای با نرخ 16 کیلوهرتز برابر با 32,000 نمونه خواهد بود.

تشخیص فعالیت صوتی (Voice Activity Detection یا VAD). وقتی کسی صحبت نمی‌کند، جلوی پردازش‌های مراحل بعدی را می‌گیرد. مدل Silero VAD 4.0 (سال 2024) به ازای هر فریم 30 میلی‌ثانیه‌ای، کمتر از 1 میلی‌ثانیه زمان روی CPU می‌برد. کتابخانه‌ی webrtcvad گزینه‌ی قدیمی‌تر آن است.

تبدیل گفتار به متن استریمینگ (Streaming ASR). مدل‌هایی که هم‌زمان با رسیدن داده‌های صوتی، متن‌های پیاده‌شده‌ی جزئی را تولید می‌کنند. مدل Parakeet-CTC-0.6B در حالت استریمینگ (از فریم‌ورک NeMo، سال 2024) با تاخیر 320 میلی‌ثانیه به نرخ خطای کلمات (WER) 2 تا 5 درصد می‌رسد. مدل Whisper-Streaming (توسط Macháček و همکاران، 2023) با تکه‌تکه کردن ورودی‌های Whisper به تاخیر شبه‌استریمینگ حدود 2 ثانیه دست می‌یابد.

وقفه و پریدن در صحبت (Interruption یا Barge-in). وقتی کاربر وسط صحبت دستیار شروع به حرف زدن می‌کند، سیستم باید (الف) این ورود را تشخیص دهد، (ب) خروجی TTS را متوقف کند، و (ج) بقیه‌ی خروجی مدل زبانی را دور بریزد. همه‌ی این کارها باید ظرف 100 میلی‌ثانیه انجام شوند، وگرنه کاربر احساس می‌کند دستیار کر است و صدایش را نمی‌شنود.

بستر انتقال WebRTC Opus. فریم‌های 20 میلی‌ثانیه‌ای، نرخ نمونه‌برداری 48 کیلوهرتز و بیت‌ریت تطبیقی بین 8 تا 128 کیلوبیت بر ثانیه. این استاندارد وب و موبایل است. در سال 2026 ابزارهای LiveKit، Daily.co و Pion استک‌های اصلی ساخت اپلیکیشن‌های صوتی هستند.

بافر لرزش (Jitter buffer). بسته‌های شبکه معمولاً با تاخیر یا نامرتب به مقصد می‌رسند. بافر لرزش این بسته‌ها را بازآرایی و هموار می‌کند؛ اگر خیلی کوچک باشد به وقفه‌های صوتی شنیدنی می‌انجامد، و اگر خیلی بزرگ باشد تاخیر ایجاد می‌کند. بازه‌ی معمولی آن 60 تا 80 میلی‌ثانیه است.

خطاهای رایج

  • تداخل تردها (Thread contention). قفل مفسر سراسری (GIL) در پایتون در کنار مدل‌های سنگین می‌تواند ترد پردازش صدا را از منابع محروم کند. از کتابخانه‌های صوتی مبتنی بر کالبک‌های C (مانند sounddevice یا PortAudio) استفاده کنید و پایتون را از مسیر بحرانی دور نگه دارید.
  • تاخیر تبدیل نرخ نمونه‌برداری (Sample-rate conversion latency). تغییر نرخ نمونه‌برداری در میانه‌ی پایپ‌لاین بین 5 تا 20 میلی‌ثانیه تاخیر اضافه می‌کند. این تبدیل را در همان مرحله‌ی اول ورودی انجام دهید یا از نمونه‌گیرهایی با تاخیر صفر (مثل PolyPhase یا soxr_hq) استفاده کنید.
  • آماده‌سازی اولیه (TTS priming). حتی موتورهای سریع TTS مثل Kokoro هم در اولین درخواست 100 تا 200 میلی‌ثانیه زمان برای گرم شدن مدل هدر می‌دهند. مدل را کَش کنید و پیش از اولین نوبت واقعی مکالمه، با یک اجرای آزمایشی گرم نگه‌اش دارید.
  • حذف اکو (Echo cancellation). بدون AEC (حذف اکوی صوتی)، صدای خروجی اسپیکر دوباره وارد میکروفون می‌شود و مدل ASR شروع به پیاده‌سازی صدای خودِ بات می‌کند. کتابخانه‌ی WebRTC AEC3 گزینه‌ی استاندارد متن‌باز برای این کار است.
نایکوئیست و الیاسینگ (تداخل فرکانسی)فرکانس و نرخ نمونه‌برداری را بکشید
NYQUIST & ALIASINGGrey is the true signal, blue dots are the samples, the dashed line is the wave those dots imply. As long as the sample rate exceeds twice the signal frequency, the samples reconstruct the original. Push the frequency past Nyquist (fs/2) and the same dots trace a lower, false frequency: that is aliasing, and it is why audio is low-pass filtered before sampling.
رنگ خاکستری سیگنال اصلی است، نقاط آبی نمونه‌ها هستند و خط‌چین موجی است که این نقاط بازنمایی می‌کنند. تا زمانی که نرخ نمونه‌برداری بیش از دو برابر فرکانس سیگنال باشد، نمونه‌ها سیگنال اصلی را بازسازی می‌کنند. اگر فرکانس را push کنید و از حد نایکویست (fs/2) عبور دهید، همان نقاط فرکانس پایین‌تر و کاذبی را نشان می‌دهند: این همان الیاسینگ (تداخل فرکانسی) است، و دلیل فیلتر پایین‌گذر صدا قبل از نمونه‌برداری نیز همین است.

پیاده‌سازی از صفر

مرحله‌ی 1: بافر حلقوی

import collections

class RingBuffer:
    def __init__(self, capacity):
        self.buf = collections.deque(maxlen=capacity)
    def write(self, frame):
        self.buf.extend(frame)
    def read(self, n):
        return [self.buf.popleft() for _ in range(min(n, len(self.buf)))]
    def level(self):
        return len(self.buf)

ظرفیت این بخش سقف تاخیر بافرسازی را مشخص می‌کند. 32,000 نمونه در نرخ 16 کیلوهرتز معادل 2 ثانیه است.

مرحله‌ی 2: گیت VAD

def simple_energy_vad(frame, threshold=0.01):
    return sum(x * x for x in frame) / len(frame) > threshold ** 2

در محیط عملیاتی آن را با Silero VAD جایگزین کنید:

import torch
vad, _ = torch.hub.load("snakers4/silero-vad", "silero_vad")
is_speech = vad(torch.tensor(frame), 16000).item() > 0.5

مرحله‌ی 3: ASR استریمینگ

# استریمینگ Parakeet-CTC-0.6B از طریق NeMo
from nemo.collections.asr.models import EncDecCTCModelBPE
asr = EncDecCTCModelBPE.from_pretrained("nvidia/parakeet-ctc-0.6b")
# chunk_ms=320 ms, look_ahead_ms=80 ms
for chunk in audio_stream():
    partial_text = asr.transcribe_streaming(chunk)
    print(partial_text, end="\r")

مرحله‌ی 4: مدیریت وقفه و پریدن در صحبت

class Dialog:
    def __init__(self):
        self.tts_task = None

    def on_user_speech(self, frame):
        if self.tts_task and not self.tts_task.done():
            self.tts_task.cancel()   # barge-in
        # سپس ورودی به ASR استریمینگ داده شود

    def on_final_user_utterance(self, text):
        self.tts_task = asyncio.create_task(self.reply(text))

    async def reply(self, text):
        async for tts_chunk in llm_then_tts(text):
            speaker.write(tts_chunk)

پیاده‌سازی این بخش کاملاً به ورودی/خروجی ناهمگام (Async I/O) و استریمینگ لغوپذیرِ TTS وابسته است. متد peerconnection.stop() روی ترک صوتی در WebRTC راهکار استاندارد این کار به شمار می‌رود.

استفاده از ابزارهای واقعی

استک پیشنهادی سال 2026:

لایه ابزار انتخابی
بستر انتقال داده LiveKit (مبتنی بر WebRTC) یا Pion (زبان Go)
VAD Silero VAD 4.0
ASR استریمینگ Parakeet-CTC-0.6B یا Whisper-Streaming
مدل زبانی (تولید اولین توکن) Groq، Cerebras یا vLLM-streaming
TTS استریمینگ Kokoro یا ElevenLabs Turbo v2.5
حذف اکو WebRTC AEC3
سرتاسری و نیتیو (End-to-end) OpenAI Realtime API یا Moshi

دام‌های رایج

  • بافرسازی 500 میلی‌ثانیه‌ای برای اطمینان بیشتر. این بافر دقیقاً همان کف تاخیر سیستم شماست؛ آن را کاهش دهید.
  • پین نکردن تردها. اجرای کالبک صدا روی تردی با اولویت کمتر از رابط کاربری (UI)، زیر بار پردازشی باعث سکته و پرش صدا می‌شود.
  • چانک‌های خیلی کوچک در TTS. قطعات صوتی کمتر از 200 میلی‌ثانیه در مرحله‌ی وکودر (Vocoder) نویزهای صوتی محسوسی تولید می‌کنند. چانک‌های 320 میلی‌ثانیه‌ای بهترین تعادل را دارند.
  • نبود بافر لرزش. شبکه‌های واقعی همیشه لرزش دارند؛ بدون هموارسازی، صدا دچار صدای تَق‌تَق خواهد شد.
  • مدیریت خطای ساده‌انگارانه. پایپ‌لاین‌های صوتی باید ضدکرش باشند. بروز یک خطا نباید کل نشست مکالمه را از بین ببرد.

خروجی این درس

نتیجه را در فایل outputs/skill-realtime-designer.md ذخیره کنید. یک پایپ‌لاین صوتی بلادرنگ را همراه با مشخص کردن سقف تاخیر دقیق هر مرحله طراحی کنید.

تمرین‌ها

  1. ساده. اسکریپت code/main.py را اجرا کنید. این کد یک بافر حلقوی همراه با VAD مبتنی بر سطح انرژی را شبیه‌سازی می‌کند و تاخیر هر مرحله را برای یک استریم آزمایشی 10 ثانیه‌ای چاپ می‌نماید.
  2. متوسط. با استفاده از کتابخانه‌ی sounddevice، یک حلقه‌ی مستقیم (Passthrough) بسازید که صدای میکروفون را در قالب فریم‌های 20 میلی‌ثانیه‌ای دریافت کند و وضعیت VAD را در هر فریم نمایش دهد.
  3. دشوار. یک تست اکوی دوطرفه‌ی کامل با aiortc پیاده‌سازی کنید: مرورگر ← WebRTC ← پایتون ← WebRTC ← مرورگر. سپس تاخیر رفت‌وبرگشت صدا از خروجی تا ورودی (Glass-to-glass) را با ارسال یک پالس 1 کیلوهرتزی اندازه‌گیری کنید.

اصطلاحات کلیدی

اصطلاح در گفت‌وگوهای عامیانه معنای دقیق فنی
بافر حلقوی (Ring buffer) صف چرخشی صف FIFO با اندازه‌ی ثابت و بدون نیاز به قفل (یا با قفل SPSC) برای فریم‌های صوتی.
VAD فیلتر سکوت مدل یا روش ابتکاری (Heuristic) که کلام را از سکوت یا نویز تفکیک می‌کند.
ASR استریمینگ تبدیل گفتار به متن بلادرنگ مدلی با پنجره‌ی پیش‌نگری محدود که هم‌گام با دریافت صدا، متن جزئی را تولید می‌کند.
بافر لرزش (Jitter buffer) هموارساز شبکه صفی که بسته‌های نامرتب شبکه را مرتب می‌کند؛ عموماً بین 60 تا 80 میلی‌ثانیه.
حذف اکو (AEC) حذف بازگشت صدا حذف سیگنال بازگشتی اسپیکر به میکروفون.
پریدن در صحبت (Barge-in) قطع کردن توسط کاربر قابلیتی که حین پخش TTS متوجه صحبت کاربر می‌شود و بلافاصله پخش را متوقف می‌کند.
دوطرفه‌ی کامل (Full duplex) مکالمه‌ی هم‌زمان دوطرفه قابلیتی که در آن کاربر و بات می‌توانند هم‌زمان صحبت کنند؛ مثل مدل Moshi.

برای مطالعه‌ی بیشتر

سؤالات متداول

سقف تاخیر مجاز برای طبیعی به نظر رسیدن یک مکالمه چقدر است؟

تاخیر پاسخ‌دهی انسان‌ها حدود 230 میلی‌ثانیه است؛ بنابراین مجموع زمان بافر، VAD، تبدیل گفتار، مدل زبانی و سنتز صدا نباید از 400 تا 500 میلی‌ثانیه تجاوز کند.

چرا استفاده از بافر حلقوی (Ring Buffer) در پردازش صوت ضروری است؟

بافر حلقوی با اندازه‌ی ثابت از تخصیص مکرر حافظه در مسیر بحرانی (Hot Path) جلوگیری می‌کند و ارتباط بین ترد دریافت صدا و ترد پردازش را بدون افت کارایی برقرار می‌سازد.

قابلیت Barge-in یا وقفه در دستیار صوتی چگونه کار می‌کند؟

سیستم به محض تشخیص صدای کاربر در میانه‌ی صحبت بات از طریق VAD، در زمانی کمتر از 100 میلی‌ثانیه پخش TTS را متوقف کرده و ادامه‌ی خروجی مدل زبانی را لغو می‌کند.

ما رو توی گوگل بیشتر ببین

با افزودن نکست ادمین به «منابع ترجیحی» گوگل، درس‌ها و مطالب ما را در بخش اخبار برتر جستجو بیشتر می‌بینید.