توليد الصوت للمحادثات

Home PDF Audio

مستوحى من مقطع فيديو على YouTube يتضمن مناقشة حول DeepSeek-V3، كنت أجرب محادثات تم إنشاؤها بواسطة الذكاء الاصطناعي. هدفي هو إنشاء حوارات صوتية واقعية باستخدام Google Text-to-Speech و ffmpeg لتوليد وتسلسل الصوت. يوضح الكود التالي نهجي الحالي لمحاكاة محادثة طبيعية بين شخصين.

المهمة

قم بإنشاء محادثة طبيعية وممتدة بين خبيرين، A و B، تتكون من 100 دور على الأقل. يجب أن يناقش الخبيران موضوعًا محددًا بعمق، مع تدفق المحادثة ذهابًا وإيابًا. يجب أن يطرح المشاركون أسئلة، ويشاركون رؤى، ويستكشفون تفاصيل الموضوع. يجب أن يكون التنسيق كما يلي:

[
    {
      "speaker": "A",
      "line": "مرحبًا، لقد سمعت الكثير عن تعلم الآلة (ML)، والتعلم العميق (DL)، و GPT مؤخرًا. هل يمكنك توضيح ذلك لي؟"
    },
    {
      "speaker": "B",
      "line": "بالتأكيد! لنبدأ بالأساسيات. تعلم الآلة هو مجال من مجالات علوم الكمبيوتر حيث تتعلم الأنظمة من البيانات لتحسين أدائها دون أن تكون مبرمجة بشكل صريح. فكر في الأمر على أنه تعليم الكمبيوتر للتعرف على الأنماط."
    }
]

الكود

import os
import json
import random
import subprocess
from google.cloud import texttospeech
import tempfile
import time
import argparse

# دليل الإخراج الثابت للمحادثات
OUTPUT_DIRECTORY = "assets/conversations"
INPUT_DIRECTORY = "scripts/conversation"

def text_to_speech(text, output_filename, voice_name=None):
    print(f"جارٍ توليد الصوت لـ: {output_filename}")
    try:
        client = texttospeech.TextToSpeechClient()
        synthesis_input = texttospeech.SynthesisInput(text=text)
        voice = texttospeech.VoiceSelectionParams(language_code="en-US", name=voice_name)
        audio_config = texttospeech.AudioConfig(
            audio_encoding=texttospeech.AudioEncoding.MP3,
            effects_profile_id=["small-bluetooth-speaker-class-device"]
        )
        
        retries = 5
        for attempt in range(1, retries + 1):
            try:
                response = client.synthesize_speech(input=synthesis_input, voice=voice, audio_config=audio_config)
                with open(output_filename, 'wb') as out:
                    out.write(response.audio_content)
                print(f"تم كتابة المحتوى الصوتي في {output_filename}")
                return True
            except Exception as e:
                print(f"حدث خطأ في المحاولة {attempt}: {e}")
                if attempt == retries:
                    print(f"فشل توليد الصوت بعد {retries} محاولات.")
                    return False
                wait_time = 2 ** attempt
                print(f"إعادة المحاولة بعد {wait_time} ثوانٍ...")
                time.sleep(wait_time)
    except Exception as e:
        print(f"حدث خطأ أثناء توليد الصوت لـ {output_filename}: {e}")
        return False

def process_conversation(filename):
    filepath = os.path.join(INPUT_DIRECTORY, filename)
    output_filename = os.path.join(OUTPUT_DIRECTORY, os.path.splitext(filename)[0] + ".mp3")

    if os.path.exists(output_filename):
        print(f"ملف الصوت موجود بالفعل: {output_filename}")
        return

    try:
        with open(filepath, 'r', encoding='utf-8') as f:
            conversation = json.load(f)
    except Exception as e:
        print(f"حدث خطأ أثناء تحميل ملف المحادثة {filename}: {e}")
        return

    temp_files = []
    
    voice_options = ["en-US-Journey-D", "en-US-Journey-F", "en-US-Journey-O"]
    voice_name_A = random.choice(voice_options)
    voice_name_B = random.choice(voice_options)
    while voice_name_A == voice_name_B:
        voice_name_B = random.choice(voice_options)

    for idx, line_data in enumerate(conversation):
        speaker = line_data.get("speaker")
        line = line_data.get("line")
        if not line:
            continue
        temp_file = os.path.join(OUTPUT_DIRECTORY, f"temp_{idx}.mp3")
        temp_files.append(temp_file)
        
        voice_name = None
        if speaker == "A":
            voice_name = voice_name_A
        elif speaker == "B":
            voice_name = voice_name_B
        
        if not text_to_speech(line, temp_file, voice_name=voice_name):
            print(f"فشل توليد الصوت للسطر {idx+1} من {filename}")
            # تنظيف الملفات المؤقتة
            for temp_file_to_remove in temp_files:
                if os.path.exists(temp_file_to_remove):
                    os.remove(temp_file_to_remove)
            return

    if not temp_files:
        print(f"لم يتم توليد أي صوت لـ {filename}")
        return

    # التسلسل باستخدام ffmpeg
    concat_file = os.path.join(OUTPUT_DIRECTORY, "concat.txt")
    with open(concat_file, 'w') as f:
        for temp_file in temp_files:
            f.write(f"file '{os.path.abspath(temp_file)}'\n")
    
    try:
        subprocess.run(
            ['ffmpeg', '-f', 'concat', '-safe', '0', '-i', concat_file, '-c', 'copy', output_filename],
            check=True,
            capture_output=True
        )
        print(f"تم تسلسل الصوت بنجاح إلى {output_filename}")
    except subprocess.CalledProcessError as e:
        print(f"حدث خطأ أثناء تسلسل الصوت: {e.stderr.decode()}")
    finally:
        os.remove(concat_file)
        for temp_file in temp_files:
            os.remove(temp_file)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="معالجة ملفات JSON للمحادثات لتوليد الصوت.")
    args = parser.parse_args()

    os.makedirs(OUTPUT_DIRECTORY, exist_ok=True)

    for filename in os.listdir(INPUT_DIRECTORY):
        if filename.endswith(".json"):
            process_conversation(filename)

الغلاف

ffmpeg -i deepseek.jpg -vf "crop=854:480" deepseek_480p_cropped.jpg

الفيديو

ffmpeg -loop 1 -i deepseek.jpg -i deepseek.mp3 -c:v libx264 -tune stillimage -c:a aac -b:a 192k -shortest output_video.mp4

Back 2025.01.18 Donate