Skip to content

audio pronunciation

SalehGNUTUX edited this page Jul 20, 2026 · 1 revision

نظامُ الصوت والنطق — مرجعٌ تقنيّ

للتفصيلِ الكاملِ عن Piper (الإعدادُ من الصفر · خصائصُ كلِّ لغة · إصلاحُ النطق · التوليدُ التفاضليُّ · الحدود): piper-tts-guide.md.

مرجعٌ شاملٌ لطبقات الصوت في «مِشكاة»، مع وصفةِ علاجِ مشكلات النطق (ترتيب التشكيل، لفظ الجلالة، حدود المحرّكات). كلُّ النطق محلّيٌّ 100%: مقاطع MP3 ثابتةٌ تُولَّد وقتَ التطوير، وتُشغَّل دون إنترنت أو ذكاءٍ وقتَ التشغيل.

1) الطبقات والمصادر

الطبقة الملفّ الدور
التفضيلات src/sound-prefs.js isTonesOn/isVoiceOn + اختيارُ مجموعة الصوت لكلِّ نوع (letter/word/sentence/story)
التوجيه src/tts-clips.js classify(text) ثمّ srcFor يختار المصدر بحسب المجموعة المحدّدة، ويرتدُّ لـespeak إن غاب المقطع
النطق src/speak.js يجرّب المقطعَ المضمون أولاً ثمّ Web Speech للنصوص الطويلة؛ مهلةُ أمانٍ تُطلق onend إن لم تَجهز أصواتُ المتصفّح
التهجئة src/spell.js segment(text) يحلّل الكلمةَ لمقاطع، playSpelled يشغّلها بالتتابع ثمّ الكلمة كاملة
التسجيل البشريّ (مشترَك) src/voices.js + voices-bundled.json نموذجُ الصوت الذي يبنيه الأهل في record.html (IndexedDB tilmithi_voices، عامٌّ للجميع)
تسجيل التمرّن (لكلِّ حساب) src/practice.js + src/reader-recorder.js الطفل يسجّل قراءتَه ويستمع، محفوظٌ في حسابه وحده

مصادرُ المقاطع المُولَّدة (وقتَ التطوير فقط)

  • espeak‑ng (آليّ): npm run gen:audiotools/gen-audio.mjspublic/tts/*.mp3 + src/tts-manifest.json (نصّ → ملفّ).
  • Piper العصبيّ (عربيّ ar_JO-kareem): npm run gen:piper → كلمات/جمل/قصص → مجموعة tts-kareem في voices-bundled.json.
  • Piper اللغات الأجنبيّة (en_GB-alba/fr_FR-tom): node tools/gen-piper-lang.mjs en|fr.
  • المصدرُ الموحَّد للنصوص: src/vocab.js (يقرؤه الاستوديو والمولّدات معاً، فإضافةُ المحتوى تتزامن تلقائيّاً).

2) ⭐ توحيدُ ترتيب التشكيل ولفظ الجلالة — src/arabic-normalize.js

المشكلة (الجذر)

محرّكا النطق (espeak والعصبيُّ Piper الذي يعتمد espeak في التفنيم) يتوقّعان ترتيب «حرف + شدّة + حركة». لكنّ كثيراً من النصوص (وما يُنتجه تطبيعُ Unicode NFC) يأتي بترتيب «حرف + حركة + شدّة»، فتضيع الشدّة وتُسمَع كالسكون:

الكلمة ترتيب «حركة+شدّة» (خطأ) ترتيب «شدّة+حركة» (صحيح)
أوّل ʔˈauːl (أَوْل) ʔˈauːˌal (أوّل)
محرّم mˈuħarrm mˈuħarram
عدّ ʕˈadː ʕˈadːa
جنّة dʒˈannt dʒˈannat

وكذلك لفظُ الجلالة المشكَّل يُشوَّه: اللَّهِ → ʔˈalllhi (تُسمَع «lhi»)، لأنّ قاعدةَ espeak الخاصّةَ لا تعملُ إلّا للكلمة المجرّدة وحدها دون حركةِ إعراب.

الحلّ

دالّتان في src/arabic-normalize.js (نقيّةٌ بلا DOM، تُقرأ في Node والمتصفّح):

  1. reorderMarks(text)NFC ثمّ تبديلُ كلِّ تتابع «حركة/تنوين + شدّة» إلى «شدّة + حركة/تنوين». (NFC يضع الحركةَ قبل الشدّة دائماً بسبب الـccc، فالتبديلُ بعده يُنتج الترتيبَ الصحيحَ مهما كان الإدخال.)
  2. jalalahBare(text) — يعالج كلَّ كلمةٍ على حدة: إن طابق هيكلُها المجرّد لفظَ الجلالة (الله/لله/بالله/تالله/والله/كالله/اللهمّ) تُستبدَل بإملاءٍ صوتيٍّ بمدٍّ صريح مع الحفاظ على حركة الإعراب الأخيرة:
المُدخَل يُركَّب صوتيّاً النطق
اللهِ / اللهُ / اللهَ اَلّاهِ / اَلّاهُ / اَلّاهَ ʔallaːhi/u/a
لله (الحمد لله) لِلّاه + الحركة lillaːhi
بالله بِلّاه + الحركة billaːhi
اللهمّ اَلّاهُمّ ʔallaːhumm

forSynthesis(text) = jalalahBare(reorderMarks(text)).

أين يُطبَّق (مبدأٌ حاسم)

يُطبَّق على نصّ التركيب الصوتيّ فقط، بينما مفتاحُ البحث يبقى النصَّ الأصليَّ — فلا يتغيّر اللوكب وقتَ التشغيل، ويتحسّن الصوتُ المُولَّد فقط:

  • tools/gen-audio.mjs → داخل spokenFor (المفتاح = الأصل، المنطوق = forSynthesis).
  • tools/gen-piper.mjstext: forSynthesis(t) مع بقاء key/out على الأصل.
  • src/spell.js segment() → يطبّق reorderMarks على كلِّ مقطع ليطابقَ مفاتيحَ المقاطع المُسجَّلة/المُولَّدة (التي تستعمل ترتيبَ syl.js: «شدّة+حركة»).

بعد أيِّ تعديلٍ يمسُّ نطقَ الشدّة/الجلالة

npm run gen:audio   # espeak (يتطلّب espeak-ng + lame)
npm run gen:piper   # العصبيّ kareem (يتطلّب .piper-venv + النموذج)
npm run build

التحقّق التجريبيّ (بلا تشغيلٍ صوتيّ) عبر فونيمات espeak:

espeak-ng -v ar -q --ipa -- "$(node -e 'import("./src/arabic-normalize.js").then(m=>process.stdout.write(m.forSynthesis("اللَّهِ")))')"
# المتوقَّع: ʔˈallaːhˌi

3) حدودُ المحرّكات (مزالقُ معروفة)

  • مطابقةُ المقاطع حرفيّة: playClip(text) والتسجيلاتُ تطابق النصَّ تماماً. أيُّ اختلافٍ (تشكيلٌ، ترتيبٌ، مسافة) ⇒ ارتدادٌ لـWeb Speech أو صمت. النصوصُ المركّبةُ وقتَ التشغيل لا تجد مقطعاً.
  • أصواتٌ ضعيفةٌ في espeak: الحلق/اللهاة (العين، الغين) تُطال فونيميّاً في gen-audio (weakFix)؛ السكونُ على ب/ج/د/ض والهمزة أْ = صمتٌ تقريبيّ؛ نِ تلتبس بـذِ؛ الشدّةُ تقريبيّة. الحلُّ الجذريُّ: تسجيلٌ بشريّ (الاستوديو/public/tts/custom/).
  • النماذجُ العصبيّةُ للحروف المفردة: Piper ضعيفٌ للحرف المنفرد (طبيعتُه للجُمل)؛ لذا الحروفُ مستثناةٌ من المجموعات العصبيّة (types) وافتراضُها espeak. في اللغات الأجنبيّة يُنطَق اسمُ الحرف ككلمة (حقل name: H→aitch) لا الحرفُ المجرّد.
  • مزلقُ ترتيب التشكيل: انظر القسم ٢ أعلاه — أهمُّ سببٍ لتشويه النطق المُولَّد.
  • أمانُ Node: ما يستورده المولّدون (vocab.js/syl.js/robo-phrases.js/islamic.js/arabic-normalize.js) يجب أن يخلوَ من window/DOM.

4) النسخُ الوعديّة وتزامنُ النموذج

playUrlAsync (في tts-clips) ينتظر جهوزيّةَ المقطع (canplay) قبل التشغيل فلا يفشلُ أوّلُ تشغيلٍ لمقطعٍ لم يُحمَّل (كان يرتدُّ خطأً للنطق الآليّ). ويحترمُ النموذجَ المختار (عصبيّ/بشريّ/آليّ) دون استبداله بسبب توقيت التحميل. قارئُ القصص (story-reader.js) يُهيّئ المجموعةَ (primeVoices) قبل أوّل تشغيلٍ تفادياً للارتداد.

انظر أيضاً: pitfalls-and-solutions.md · architecture.md · ../CLAUDE.md.

Clone this wiki locally