مقاله آموزشی

تبدیل صوت فارسی به متن با API؛ پیاده‌سازی و زیرنویس خودکار

نویسنده: تیم نِت اَرز ۱۰ دقیقه مطالعه ۶۳ بازدید

یک جلسهٔ دوساعته ضبط کرده‌اید و باید متنش را داشته باشید، یا صد ویدیوی آموزشی دارید که زیرنویس فارسی می‌خواهند. اگر دنبال راه‌حل بگردید، به دو چیز می‌رسید: سایت‌هایی که تا سه دقیقه رایگان‌اند و بعد پولی می‌شوند، و فهرست‌هایی از «۱۰ ابزار برتر» که هیچ‌کدام نمی‌گویند فایل بلندتر از سقف حجم را چه کنید، تایم‌کد را از کجا بیاورید و یک ساعت صوت دقیقاً چقدر خرج برمی‌دارد. این مقاله همان جاهای خالی را پر می‌کند، با کدی که اجرا می‌شود.

همهٔ نمونه‌ها روی درگاه API هوش مصنوعی نِت اَرز نوشته شده‌اند که با فرمت OpenAI سازگار است؛ مسیر تبدیل گفتار به متن همان /audio/transcriptions استاندارد است. روی این درگاه امروز مدل‌های OpenAI فعال است و بقیهٔ ارائه‌دهنده‌ها در حال اتصال‌اند.

کدام مدل برای فارسی و چه خروجی‌هایی می‌دهد؟

سه انتخاب عملی روی میز است و تفاوتشان بیشتر از «دقت» به فرمت خروجی برمی‌گردد؛ چیزی که وقتی زیرنویس می‌سازید تعیین‌کننده است. اعداد زیر نرخ رسمی سرویس و تقریبی‌اند؛ نرخ لحظه‌ای روی حساب شما در صفحهٔ مدل‌ها است.

مدلفرمت‌های خروجیتایم‌کد جزئینرخ رسمی تقریبییک ساعت صوت
whisper-1json، text، srt، vtt، verbose_jsonدارد (segments)حدود ۰٫۰۰۶ دلار برای هر دقیقهحدود ۰٫۳۶ دلار
gpt-4o-transcribejson، textنداردبر مبنای توکن صوتی، تقریباً معادل ۰٫۰۰۶ دلار در دقیقهحدود ۰٫۳۶ دلار
gpt-4o-mini-transcribejson، textنداردبر مبنای توکن صوتی، تقریباً معادل ۰٫۰۰۳ دلار در دقیقهحدود ۰٫۱۸ دلار

نتیجهٔ عملی این جدول ساده است: اگر زیرنویس می‌خواهید، whisper-1 انتخاب کنید چون تنها گزینه‌ای است که تایم‌کد قطعه‌به‌قطعه می‌دهد. اگر فقط متن خام می‌خواهید و صدا نویزی است، مدل‌های gpt-4o-transcribe معمولاً پاکیزه‌تر می‌نویسند. و اگر حجم کارتان زیاد است، نسخهٔ mini با نصف هزینه برای متن خام گزینهٔ منطقی است.

اولین درخواست: از یک فایل تا یک فایل SRT

ساده‌ترین حالت، گرفتن مستقیم زیرنویس است. سرویس خودش فایل SRT را برمی‌گرداند و لازم نیست چیزی بسازید:

curl https://netarz.ir/api/ai/v1/audio/transcriptions \
  -H "Authorization: Bearer $NETARZ_AI_KEY" \
  -F file=@meeting.mp3 \
  -F model=whisper-1 \
  -F language=fa \
  -F temperature=0 \
  -F response_format=srt \
  -o meeting.srt

پارامتر language=fa را همیشه بفرستید. بدون آن مدل باید زبان را حدس بزند و در فایل‌های کوتاه یا نویزی گاهی فارسی را با زبان دیگری اشتباه می‌گیرد و خروجی بی‌ربط می‌دهد. temperature=0 هم خروجی را پایدارتر می‌کند.

در پایتون، اگر می‌خواهید بعداً روی قطعه‌ها کار کنید، verbose_json بگیرید:

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://netarz.ir/api/ai/v1",
    api_key=os.environ["NETARZ_AI_KEY"],
)

with open("meeting.mp3", "rb") as audio:
    result = client.audio.transcriptions.create(
        model="whisper-1",
        file=audio,
        language="fa",
        temperature=0,
        response_format="verbose_json",
        prompt="نِت اَرز، ووکامرس، لاراول، ای‌پی‌آی، کش‌بک",
    )

print(result.text[:500])

for segment in result.segments[:5]:
    print(round(segment.start, 2), round(segment.end, 2), segment.text)

آن پارامتر prompt ترفند کوچکی است که خیلی‌ها نمی‌شناسند: فهرستی از اسامی خاص، برند و اصطلاحات تخصصی جلسه را در آن بنویسید تا مدل املای درست را انتخاب کند. برای متن‌های تخصصی، همین یک خط تفاوت محسوسی در کیفیت خروجی ایجاد می‌کند.

فایل بلندتر از سقف حجم را چطور بفرستیم؟

مسیر تبدیل گفتار به متن سقف حجم دارد (حدود ۲۵ مگابایت برای هر فایل). یک پادکست یک‌ساعته با کیفیت معمولی از این سقف رد می‌شود، پس دو کار باید بکنید: اول فشرده‌سازی، بعد تکه‌بندی. برای گفتار، صدای تک‌کاناله با نرخ نمونه‌برداری ۱۶ کیلوهرتز کاملاً کافی است و حجم را چند برابر کم می‌کند:

# استخراج صدا از ویدیو و فشرده‌سازی مناسب گفتار
ffmpeg -i lecture.mp4 -vn -ac 1 -ar 16000 -b:a 64k lecture.mp3

# تکه‌بندی به قطعه‌های ده دقیقه‌ای بدون رمزگذاری دوباره
ffmpeg -i lecture.mp3 -f segment -segment_time 600 -c copy part_%03d.mp3

حالا هر قطعه را جدا می‌فرستید و مهم‌ترین نکته این است: تایم‌کدهای هر قطعه از صفر شروع می‌شوند، پس باید به آن‌ها آفست اضافه کنید وگرنه زیرنویس نهایی کاملاً بی‌ربط می‌شود.

import glob

CHUNK_SECONDS = 600
segments = []

for index, path in enumerate(sorted(glob.glob("part_*.mp3"))):
    offset = index * CHUNK_SECONDS

    with open(path, "rb") as audio:
        result = client.audio.transcriptions.create(
            model="whisper-1",
            file=audio,
            language="fa",
            temperature=0,
            response_format="verbose_json",
        )

    for segment in result.segments:
        text = segment.text.strip()
        if text:
            segments.append({
                "start": segment.start + offset,
                "end": segment.end + offset,
                "text": text,
            })

print(len(segments), "segments")

یک هشدار عملی: -c copy برش را دقیقاً روی مرز فریم انجام نمی‌دهد و ممکن است یک کلمه بین دو قطعه نصف شود. اگر دقت برایتان حیاتی است، به‌جای برش کور، از سکوت‌ها برش بزنید یا قطعه‌ها را با چند ثانیه هم‌پوشانی بسازید و در ادغام، جمله‌های تکراری مرزی را حذف کنید.

ساخت فایل SRT با تایم‌کد درست

وقتی قطعه‌ها را یکی کردید، تبدیلشان به SRT چند خط کد است. قالب SRT ساده است: شماره، بازهٔ زمانی با ویرگول برای میلی‌ثانیه، متن، و یک خط خالی.

def to_timestamp(seconds: float) -> str:
    total = int(round(seconds * 1000))
    hours, total = divmod(total, 3600000)
    minutes, total = divmod(total, 60000)
    secs, millis = divmod(total, 1000)
    return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}"


with open("lecture.srt", "w", encoding="utf-8") as out:
    for number, segment in enumerate(segments, start=1):
        start = to_timestamp(segment["start"])
        end = to_timestamp(segment["end"])
        out.write(f"{number}\n{start} --> {end}\n{segment['text']}\n\n")

دو نکتهٔ زیرنویس فارسی که معمولاً از قلم می‌افتد: اول، فایل را حتماً با کدگذاری UTF-8 ذخیره کنید وگرنه در بعضی پخش‌کننده‌ها به‌جای متن، علامت سؤال می‌بینید. دوم، قطعه‌های طولانی‌تر از حدود ۷ ثانیه یا ۸۰ کاراکتر را بشکنید؛ خواندنشان روی صفحه سخت است. برای ویدیوهای فارسی، جهت راست‌به‌چپ را هم پخش‌کننده تشخیص می‌دهد، اما اگر متن انگلیسی و فارسی مخلوط شده باشد گاهی ترتیب نمایش به هم می‌ریزد و باید دستی اصلاح کنید.

دقت فارسی؛ صادقانه کجا خوب است و کجا نه

این بخشی است که صفحه‌های تبلیغاتی از آن عبور می‌کنند. تجربهٔ واقعی چنین است:

  • خوب کار می‌کند: صدای تمیز، یک گوینده، میکروفن نزدیک، لحن رسمی. پادکست ضبط‌شده در اتاق، ویدیوی آموزشی، ویس‌های واضح.
  • ضعیف‌تر می‌شود: لهجه‌های محلی پررنگ، صدای تلفنی، نویز محیط، چند نفر که همزمان حرف می‌زنند. در جلسهٔ چندنفره‌ای که وسط حرف هم می‌پرند، بخش‌هایی از متن جا می‌افتد یا در هم می‌رود.
  • خطاهای مشخص: اسامی خاص، اعداد و واحدها، و اصطلاحات انگلیسی که وسط جملهٔ فارسی گفته می‌شوند (مثلاً «دیتابیس» یا «دیپلوی») گاهی نادرست نوشته می‌شوند. پارامتر prompt بخش زیادی از این مورد را جبران می‌کند.
  • وجود ندارد: تفکیک گوینده. این مسیر برچسب «گویندهٔ ۱ / گویندهٔ ۲» تولید نمی‌کند؛ اگر به آن نیاز دارید باید از ابزار جداگانهٔ diarization استفاده کنید.

اگر می‌خواهید عدد داشته باشید نه حس، پنج دقیقه از یک فایل نمایندهٔ آرشیوتان را خودتان دستی پیاده کنید و با خروجی مدل مقایسه کنید؛ شمردن تعداد کلمه‌های غلط تقسیم بر کل کلمه‌ها، همان معیار نرخ خطای کلمه است و بلافاصله به شما می‌گوید بازبینی انسانی چقدر کار می‌برد. اگر روی نمونهٔ خودتان خطا زیر چند درصد بود، پردازش انبوه منطقی است؛ اگر خیلی بالاتر بود، پیش از هر چیز کیفیت ضبط را درست کنید — میکروفن نزدیک‌تر و حذف نویز، بیشتر از عوض کردن مدل جواب می‌دهد.

یک لایهٔ اصلاح سبک هم کمک می‌کند: متن خام را به یک مدل چت بدهید و صریح بخواهید فقط نگارش، فاصله‌گذاری و علائم را درست کند و هیچ جمله‌ای اضافه یا حذف نکند. اگر این قید را نگذارید، مدل شروع می‌کند به «بهتر کردن» متن و شما دیگر رونوشت واقعی جلسه را ندارید. برای پروژه‌های حساس، همیشه یک بازبینی انسانی روی خروجی نهایی بگذارید.

هزینه و پردازش انبوه

حساب سرانگشتی راحت است: با نرخ رسمی حدود ۰٫۰۰۶ دلار در دقیقه، یک ساعت صوت حدود ۰٫۳۶ دلار و ده ساعت آرشیو حدود ۳٫۶ دلار می‌شود. یعنی زیرنویس کردن یک دورهٔ آموزشی بیست‌ساعته، در حد چند دلار هزینه دارد؛ چیزی که در مقایسه با پیاده‌سازی دستی قابل مقایسه نیست. نرخ تومانی معادل، لحظه‌ای است و در صفحهٔ مدل‌ها نمایش داده می‌شود؛ منطق تبدیل توکن و مصرف به هزینه را هم در راهنمای محاسبهٔ هزینهٔ API نوشته‌ایم.

برای پردازش آرشیو بزرگ، سه توصیهٔ عملی: قطعه‌ها را موازی نفرستید مگر با محدودیت همزمانی (مثلاً حداکثر سه درخواست همزمان) تا به سقف نرخ نخورید؛ نتیجهٔ هر قطعه را بلافاصله روی دیسک ذخیره کنید تا اگر اسکریپت وسط کار قطع شد از صفر شروع نکنید؛ و یک فایل وضعیت ساده نگه دارید تا قطعه‌های پردازش‌شده دوباره ارسال نشوند. اگر پروژه‌تان لاراول است، همین حلقه را داخل صف ببرید — الگویش را در راهنمای API هوش مصنوعی در PHP و لاراول آورده‌ایم.

قدم بعدی

اگر هدفتان صرفاً متن جلسه یا زیرنویس خام است، همین مسیر کافی است و با چند دلار اعتبار می‌توانید آرشیو کاملتان را پردازش کنید. اما اگر می‌خواهید سراغ دوبله، صداگذاری و ترجمهٔ ویدیو بروید، آنجا ابزارهای دیگری وارد می‌شوند؛ گردش کار کامل آن را جداگانه در دوبله و زیرنویس فارسی ویدیو با هوش مصنوعی نوشته‌ایم و برای تولید صدای طبیعی هم راهنمای ElevenLabs نقطهٔ شروع بهتری است.

برای اجرای کدهای این مقاله به یک کلید و کمی اعتبار نیاز دارید. روش تهیهٔ آن از داخل ایران و بدون کارت ارزی در خرید API چت‌جی‌پی‌تی در ایران و تصویر کلی سرویس در API هوش مصنوعی با پرداخت ریالی آمده است؛ مستندات مسیرها و پارامترها هم در مستندات فنی نِت اَرز است. قیمت لحظه‌ای اعتبار پیش از تأیید پرداخت نمایش داده می‌شود.

آموزش‌های کاربردی در گیسو

گیسو برند هوش مصنوعی نِت اَرز است، با همین حساب و همین اعتبار. این راهنماها قدم‌به‌قدم نشان می‌دهند همین کار را چطور با هوش مصنوعی انجام دهید.

پرسش‌های پرتکرار

برای فایل صوتی بلندتر از سقف حجم چه کنم؟

اول صدا را تک‌کاناله و با نرخ نمونه‌برداری پایین فشرده کنید که معمولاً حجم را چند برابر کم می‌کند. اگر باز هم بزرگ بود، با ffmpeg به قطعه‌های ده دقیقه‌ای بشکنید و هنگام ادغام، به تایم‌کد هر قطعه آفست زمانی همان قطعه را اضافه کنید.

دقت تبدیل صوت فارسی به متن چقدر است؟

روی صدای تمیز با یک گوینده و لحن رسمی، خروجی معمولاً با ویرایش جزئی قابل استفاده است. اما لهجهٔ محلی پررنگ، نویز محیط، صدای تلفنی و صحبت همزمان چند نفر دقت را محسوس پایین می‌آورد و در این حالت بازبینی انسانی لازم است.

می‌توانم مستقیم فایل زیرنویس SRT بگیرم؟

بله، با مدل whisper-1 کافی است پارامتر response_format را برابر srt یا vtt بگذارید تا خروجی آماده تحویل داده شود. مدل‌های خانوادهٔ gpt-4o-transcribe فقط متن و JSON برمی‌گردانند و برای زیرنویس تایم‌کددار مناسب نیستند.

آیا خروجی مشخص می‌کند هر جمله را چه کسی گفته است؟

خیر، این مسیر تفکیک گوینده انجام نمی‌دهد و همهٔ متن را یکدست برمی‌گرداند. اگر به برچسب گوینده نیاز دارید باید از ابزار جداگانهٔ diarization استفاده کنید یا هر گوینده را روی کانال صوتی جدا ضبط کنید.

محصولات مرتبط با این مقاله

مطالب مشابه

نظر خوانندگان

هنوز نظری ثبت نشده. اگر این مقاله پرسشتان را جواب داد یا جای چیزی در آن خالی ماند، همین‌جا بنویسید.

نظرتان را بنویسید

این مقاله چقدر به کارتان آمد؟ (اختیاری)

نظرها را پیش از انتشار بررسی می‌کنیم. نقد صریح مشکلی ندارد؛ تبلیغ و توهین منتشر نمی‌شود.