كيفية تحديد فترات الصمت والكلام في الصوت باستخدام WebRTC VAD في نفس اللحظة

كيفية تحديد فترات الصمت والكلام في الصوت باستخدام WebRTC VAD في نفس اللحظة

كيفية تحديد فترات الصمت والكلام في الصوت باستخدام WebRTC VAD أصبحت خطوة أساسية في تطبيقات الذكاء الاصطناعي الصوتية، مثل: التعرف على الكلام، أنظمة الرد الآلي، تسجيل المكالمات، وتطبيقات تحليل الصوت. في هذا الشرح سنركز على كيفية استخدام WebRTC VAD في بايثون لتحديد متى يكون هناك كلام ومتى يكون صمت في نفس اللحظة تقريباً (Real-time أو شبه لحظي)، مع أمثلة عملية ونصائح لتحسين النتائج.

إذا لم تكن قد جهزت بيئة بايثون من قبل، يمكنك الرجوع إلى: خطوات تثبيت بايثون على جهاز بنظام ويندوز ثم شرح البيئة الافتراضية في بايثون لتهيئة بيئة عمل مستقلة لمشروعك.

ما هو WebRTC VAD؟

VAD اختصار لـ Voice Activity Detection، أي اكتشاف وجود صوت بشري (كلام) في الإشارة الصوتية. مكتبة WebRTC VAD هي جزء من مشروع WebRTC من جوجل، وتم تصميمها لتعمل في الزمن الحقيقي لاكتشاف الكلام بدقة وسرعة معقولة.

يمكننا باستخدام WebRTC VAD تقسيم أي إشارة صوتية أو بث مباشر إلى:

  • فترات كلام (Speech): مقاطع يوجد بها صوت بشري.
  • فترات صمت أو ضوضاء بسيطة (Non-speech): لا يوجد فيها كلام واضح.

هذا التقسيم ضروري قبل تمرير الصوت إلى نموذج التعرف على الكلام (ASR) لأنه:

  • يقلل حجم البيانات المرسلة للنموذج.
  • يحسن السرعة والتكلفة.
  • يرفع الدقة؛ لأن النموذج يركز على مناطق الكلام الفعلية.

كيف يعمل WebRTC VAD بشكل مبسط؟

WebRTC VAD لا يحول الصوت إلى نص، بل يأخذ Frame صوتي قصير (مثل 10 أو 20 أو 30 ميلي ثانية) ويعيد كقيمة منطقية:

  • 1 أو True: يوجد كلام في هذا الجزء.
  • 0 أو False: لا يوجد كلام (صمت أو ضوضاء).

يعتمد في قراره على:

  • طاقة الإشارة الصوتية.
  • طيف الترددات (هل يشبه الصوت البشري؟).
  • إعداد الحساسية (Mode) التي نختارها.

لذلك، لمعرفة كيفية تحديد فترات الصمت والكلام في الصوت باستخدام WebRTC VAD، سنقوم بتقسيم الصوت إلى Frames صغيرة وتمرير كل Frame إلى VAD بشكل متتابع، ثم نجمع نتائج هذه الـ Frames لتكوين مقاطع الكلام والصمت.

المتطلبات الأساسية قبل البدء

قبل الدخول في الكود، تحتاج إلى:

  • بايثون مثبت على جهازك (يفضل 3.8 أو أعلى).
  • إنشاء بيئة افتراضية للمشروع (اختياري ولكن مستحسن) – يمكنك مراجعة: شرح البيئة الافتراضية في بايثون.
  • تثبيت مكتبة webrtcvad، ومكتبات للتعامل مع الصوت مثل pyaudio أو sounddevice في حالة العمل مع الميكروفون.

تثبيت مكتبة WebRTC VAD في بايثون

من خلال الطرفية (Terminal أو CMD):

pip install webrtcvad

للتعامل مع ملفات WAV يمكن استخدام wave المدمجة مع بايثون، وللتسجيل من الميكروفون يمكنك استخدام أحد الخيارين:

pip install pyaudio
أو
pip install sounddevice

أساسيات استخدام WebRTC VAD في بايثون

مكتبة webrtcvad في بايثون توفر كائن بسيط للعمل معه. الفكرة العامة:

  1. إنشاء كائن VAD وتحديد مستوى الحساسية (0 إلى 3).
  2. تمرير Frames صوتية بطول ثابت (10 أو 20 أو 30 مللي ثانية) بتردد عينة محدد (8000 أو 16000 أو 32000 أو 48000 هرتز) وبصيغة PCM 16-bit mono.
  3. استقبال نتيجة True/False لكل Frame.
import webrtcvad

vad = webrtcvad.Vad()
vad.set_mode(2)  # 0 أقل حساسية - 3 أعلى حساسية

# لاحقاً: vad.is_speech(frame_bytes, sample_rate)

اختيار حساسية VAD (Mode)

  • mode 0: أقل حساسية، يعتبر كثير من المقاطع صمت، مفيد في البيئات المليئة بالضوضاء.
  • mode 3: أكثر حساسية، قد يعتبر بعض الضوضاء كلاماً، لكن لا يفوت مقاطع الكلام الضعيف.

يمكنك التجربة بين 1 و 2 و 3 حتى تصل للنتيجة الأنسب لتطبيقك.

تحديد فترات الصمت والكلام من ملف صوتي

لنفترض أن لدينا ملف WAV أحادي القناة (Mono) بتردد 16000 Hz و16-bit PCM. سنقرأ الملف، نقسمه إلى Frames، ثم نستخدم WebRTC VAD لتحديد الكلام.

خطوات العمل على ملف صوتي

  1. فتح ملف WAV وقراءة البيانات الخام (Raw PCM).
  2. تقسيم البيانات إلى Frames ثابتة الطول (مثلاً 30ms).
  3. تمرير كل Frame إلى VAD.
  4. تجميع الإطارات المتتالية التي تحتوي على كلام لتكوين مقاطع الكلام (Segments).
import wave
import webrtcvad

def read_wave(path):
    with wave.open(path, 'rb') as wf:
        num_channels = wf.getnchannels()
        assert num_channels == 1, "الملف يجب أن يكون Mono"
        sample_width = wf.getsampwidth()
        assert sample_width == 2, "الملف يجب أن يكون 16-bit PCM"
        sample_rate = wf.getframerate()
        assert sample_rate in (8000, 16000, 32000, 48000), "معدل العينات غير مدعوم"
        pcm_data = wf.readframes(wf.getnframes())
        return pcm_data, sample_rate

def frame_generator(frame_duration_ms, audio, sample_rate):
    n = int(sample_rate * (frame_duration_ms / 1000.0) * 2)  # 2 بايت لكل عينة 16-bit
    offset = 0
    while offset + n <= len(audio):
        yield audio[offset:offset + n]
        offset += n

def detect_speech_segments(path, frame_duration_ms=30, mode=2):
    audio, sample_rate = read_wave(path)
    vad = webrtcvad.Vad(mode)
    frames = list(frame_generator(frame_duration_ms, audio, sample_rate))

    speech_frames = []
    segments = []
    in_speech = False
    start_index = 0

    for i, frame in enumerate(frames):
        is_speech = vad.is_speech(frame, sample_rate)

        if is_speech and not in_speech:
            # بداية مقطع كلام
            in_speech = True
            start_index = i
            speech_frames = [frame]
        elif is_speech and in_speech:
            # استمرار الكلام
            speech_frames.append(frame)
        elif not is_speech and in_speech:
            # نهاية مقطع كلام
            in_speech = False
            end_index = i
            segments.append((start_index, end_index, b''.join(speech_frames)))
            speech_frames = []

    # في حالة انتهى الملف ونحن داخل كلام
    if in_speech and speech_frames:
        segments.append((start_index, len(frames), b''.join(speech_frames)))

    # حساب الأزمنة بالثواني
    segments_with_time = []
    for start, end, data in segments:
        start_time = (start * frame_duration_ms) / 1000.0
        end_time = (end * frame_duration_ms) / 1000.0
        segments_with_time.append({
            "start": start_time,
            "end": end_time,
            "data": data
        })

    return segments_with_time

if __name__ == "__main__":
    segments = detect_speech_segments("input.wav")
    for seg in segments:
        print(f"كلام من {seg['start']:.2f} إلى {seg['end']:.2f} ثانية")

بهذا الشكل نكون قد طبقنا عملياً كيفية تحديد فترات الصمت والكلام في الصوت باستخدام WebRTC VAD على ملف صوتي كامل، وحصلنا على أزمنة بداية ونهاية كل مقطع كلام بدقة الإطار المستخدم.

استخدام WebRTC VAD في الزمن الحقيقي (من الميكروفون)

لتحديد الكلام والصمت في نفس اللحظة تقريباً من الميكروفون، نحتاج إلى:

  • قراءة الصوت من الميكروفون على شكل Frames صغيرة.
  • تمرير كل Frame مباشرة إلى VAD.
  • اتخاذ قرار سريع: هل يوجد كلام الآن أم لا؟

المثال التالي يستخدم مكتبة pyaudio:

import pyaudio
import webrtcvad
import struct
import time

RATE = 16000
FRAME_DURATION_MS = 30  # طول الإطار
FRAME_SIZE = int(RATE * FRAME_DURATION_MS / 1000)  # عدد العينات في كل إطار
CHANNELS = 1

vad = webrtcvad.Vad(2)  # حساسية متوسطة

p = pyaudio.PyAudio()

stream = p.open(
    format=pyaudio.paInt16,
    channels=CHANNELS,
    rate=RATE,
    input=True,
    frames_per_buffer=FRAME_SIZE
)

print("ابدأ التحدث (Ctrl+C للإيقاف)...")

try:
    while True:
        data = stream.read(FRAME_SIZE, exception_on_overflow=False)
        # data بالفعل Bytes 16-bit PCM Mono إذا كانت الإعدادات صحيحة
        is_speech = vad.is_speech(data, RATE)
        timestamp = time.time()
        if is_speech:
            print(f"{timestamp}: كلام")
        else:
            print(f"{timestamp}: صمت/ضوضاء")
except KeyboardInterrupt:
    print("تم الإيقاف.")
finally:
    stream.stop_stream()
    stream.close()
    p.terminate()

في هذا المثال، كل 30ms من الصوت تُسحب من الميكروفون وتُمرر إلى WebRTC VAD، ليعطيك نتيجة فورية تقريباً ما إذا كان هناك كلام أم لا. يمكنك ربط هذه النتيجة مباشرة بتسجيل الصوت، أو إرسال Frames الكلام فقط إلى خادم التعرف على الكلام، أو تشغيل منطق معين (مثلاً: بدء تسجيل عند ظهور الكلام).

استراتيجيات ذكية لتجميع النتائج وتحسين الدقة

تحديد الكلام بناءً على Frame واحدة قد يكون متذبذباً أحياناً (Frame كلام يتبعه Frame صمت وهكذا). لتحسين الاستقرار، نستخدم عادة نافذة منزلقة (Sliding Window) أو عتبات زمنية:

  • لن نعتبر أن هناك بداية كلام إلا إذا كانت مثلاً 5 Frames متتالية = كلام.
  • لن نعلن نهاية الكلام إلا إذا وجدنا مثلاً 10 Frames متتالية = صمت.

هذا يقلل التقطيع الزائد ويجعل المقاطع أكثر منطقية. مثال مبسط:

from collections import deque

def realtime_vad_with_hysteresis():
    import pyaudio, webrtcvad

    RATE = 16000
    FRAME_DURATION_MS = 30
    FRAME_SIZE = int(RATE * FRAME_DURATION_MS / 1000)
    CHANNELS = 1

    vad = webrtcvad.Vad(2)

    p = pyaudio.PyAudio()
    stream = p.open(
        format=pyaudio.paInt16,
        channels=CHANNELS,
        rate=RATE,
        input=True,
        frames_per_buffer=FRAME_SIZE
    )

    # معلمات التحكم
    speech_start_threshold = 5   # عدد الإطارات المتتالية لاعتبار بداية كلام
    speech_end_threshold = 10    # عدد الإطارات المتتالية لاعتبار نهاية كلام

    speech_buffer = deque(maxlen=speech_start_threshold)
    silence_buffer = deque(maxlen=speech_end_threshold)

    in_speech = False

    print("تحدث وسيتم اكتشاف الكلام والصمت...")

    try:
        while True:
            data = stream.read(FRAME_SIZE, exception_on_overflow=False)
            is_speech = vad.is_speech(data, RATE)

            if is_speech:
                speech_buffer.append(1)
                silence_buffer.clear()
            else:
                silence_buffer.append(1)
                speech_buffer.clear()

            if not in_speech and len(speech_buffer) == speech_start_threshold:
                in_speech = True
                print("بداية كلام")

            if in_speech and len(silence_buffer) == speech_end_threshold:
                in_speech = False
                print("نهاية كلام")

    except KeyboardInterrupt:
        print("تم الإيقاف.")
    finally:
        stream.stop_stream()
        stream.close()
        p.terminate()

if __name__ == "__main__":
    realtime_vad_with_hysteresis()

بهذه الطريقة نحصل على تحديد فترات الصمت والكلام في الصوت باستخدام WebRTC VAD بشكل أكثر استقراراً، مع إشعارات “بداية كلام” و“نهاية كلام” في نفس اللحظة تقريباً مع الحدث الفعلي.

ربط WebRTC VAD مع نماذج التعرف على الكلام

بعد أن نستطيع تقسيم الصوت إلى مقاطع كلام وصمت، يمكننا:

  • إرسال المقاطع التي تحتوي على كلام فقط إلى نموذج التعرف على الكلام (محلي أو سحابي).
  • تجاهل الصمت تماماً لتقليل التكلفة والوقت.
  • حفظ المقطع الصوتي كاملاً لكن مع تايم ستامب لفترات الكلام فقط لتحليلها لاحقاً.

إذا كنت مهتماً بتشغيل النماذج محلياً، يمكنك الاطلاع على: تشغيل نماذج الذكاء الاصطناعي محلياً باستخدام Ollama: دليل المطورين لرؤية كيف يمكن دمج التقنيات الصوتية مع نماذج لغوية تعمل على جهازك.

نصائح عملية لتحسين النتائج

  • اختيار معدل العينة المناسب: 16000 Hz غالباً كافٍ ومناسب لمعظم تطبيقات الكلام، ومدعوم جيداً في WebRTC VAD.
  • التأكد من صيغة الصوت: يجب أن يكون Mono، 16-bit PCM. إذا كان لديك ستيريو أو صيغة مختلفة، استخدم مكتبات مثل pydub أو ffmpeg للتحويل.
  • تجربة عدة قيم لـ mode: في بيئة هادئة استخدم 2 أو 3، في بيئة مليئة بالضوضاء جرّب 0 أو 1.
  • تحسين العتبات الزمنية: قيمة speech_start_threshold وspeech_end_threshold تعتمد على نوع الكلام (سريع/بطيء)، جرّب عدة قيم.
  • استخدام فلترة للضوضاء مسبقاً: في بعض الحالات يمكن أن يساعد تطبيق Noise Reduction بسيط قبل إدخال الصوت إلى VAD.

خلاصة: لماذا WebRTC VAD مهم في تطبيقات الذكاء الاصطناعي الصوتية؟

فهم كيفية تحديد فترات الصمت والكلام في الصوت باستخدام WebRTC VAD هو خطوة أساسية لأي نظام يعتمد على الصوت:

  • تطبيقات تحويل الكلام إلى نص (Speech-to-Text).
  • المساعدات الصوتية.
  • تحليل المكالمات وخدمة العملاء.
  • أنظمة المراقبة الصوتية الذكية.

WebRTC VAD يوفر لك حل جاهز وخفيف الوزن، يعمل في الزمن الحقيقي، ويمكن دمجه بسهولة مع مشاريع بايثون. من خلال تقسيم الصوت إلى Frames صغيرة وتمريرها للمكتبة، ثم استخدام بعض المنطق التجميعي (مثل النافذة المنزلقة)، يمكنك الوصول إلى كشف دقيق ومستقر لمناطق الكلام والصمت، وبالتالي تحسين أداء نماذج التعرف على الكلام بشكل ملحوظ.

إذا كنت تعمل على مشاريع بايثون أكبر، فقد تحتاج أيضاً إلى تنظيم الكود واختباره باستخدام أدوات مثل pytest لاختبار الوحدات في بايثون لضمان استقرار منطق VAD وتقطيع الصوت مع الوقت.

حول المحتوى:

تعرف على كيفية تحديد فترات الصمت والكلام في الصوت باستخدام WebRTC VAD لتحسين تقطيع الصوت ورفع دقة نماذج التعرف على الكلام، مع شرح عملي وأمثلة في بايثون.

هل كان هذا مفيدًا لك؟

أضف تعليقك