| import os |
| import re |
| import time |
| import torch |
| import torchaudio |
| import warnings |
| from pydub import AudioSegment |
|
|
| warnings.filterwarnings("ignore") |
|
|
| |
| |
| |
| original_load = torch.load |
|
|
| def safe_load(*args, **kwargs): |
| if 'map_location' not in kwargs: |
| kwargs['map_location'] = 'cpu' |
| if 'weights_only' in kwargs: |
| kwargs['weights_only'] = False |
| return original_load(*args, **kwargs) |
|
|
| torch.load = safe_load |
| |
|
|
| try: |
| from chatterbox.mtl_tts import ChatterboxMultilingualTTS |
| from phonikud_onnx import Phonikud |
| from phonikud import lexicon |
| except ImportError: |
| print("❌ שגיאה: חסרות ספריות. וודא שאתה ב-venv_fix והרצת pip install.") |
| exit() |
|
|
| class TTSManager: |
| def __init__(self, reference_audio_path="new_teacher_reference.wav"): |
| print("⚙️ טוען מנוע 'המורה למתמטיקה' (ZipVoice Mode)...") |
| self.device = "cpu" |
| self.ref_audio_path = reference_audio_path |
| self.phonikud_model = Phonikud("phonikud-1.0.int8.onnx") |
| self.tts_model = ChatterboxMultilingualTTS.from_pretrained(device=self.device) |
| print("✅ המנוע מוכן.") |
|
|
| def _split_to_sentences(self, text): |
| return [s.strip() for s in re.split(r'(?<=[.!?]) +', text) if s.strip()] |
|
|
| def generate_audio(self, text, output_filename="welcome_speech_v2.wav"): |
| sentences = self._split_to_sentences(text) |
| print(f"📝 מעבד {len(sentences)} משפטים קצרים (למניעת 'נשימות')...") |
| |
| combined = AudioSegment.empty() |
| silence = AudioSegment.silent(duration=250) |
|
|
| for i, sentence in enumerate(sentences): |
| temp_file = f"temp_part_{i}.wav" |
| print(f"🎙️ משפט {i+1}/{len(sentences)}: {sentence[:30]}...") |
| |
| |
| voweled = self.phonikud_model.add_diacritics(sentence) |
| voweled = re.sub(fr"[{lexicon.NON_STANDARD_DIAC}]", "", voweled) |
| |
| |
| voweled = voweled.replace("מוֹרֶה", "מוֹרָה").replace("מּוֹרֶה", "מּוֹרָה") |
| |
| |
| wav = self.tts_model.generate( |
| voweled, |
| language_id="he", |
| audio_prompt_path=self.ref_audio_path, |
| cfg_weight=0.90 |
| ) |
| torchaudio.save(temp_file, wav, self.tts_model.sr) |
| combined += AudioSegment.from_wav(temp_file) + silence |
| os.remove(temp_file) |
|
|
| combined.export(output_filename, format="wav") |
| print(f"✨ הושלם! הקובץ מחכה ב: {output_filename}") |
|
|
| if __name__ == "__main__": |
| REFERENCE_FILE = "new_teacher_reference.wav" |
| |
| if not os.path.exists(REFERENCE_FILE): |
| print(f"❌ שגיאה: קובץ דגימה '{REFERENCE_FILE}' לא נמצא.") |
| else: |
| tts = TTSManager(reference_audio_path=REFERENCE_FILE) |
| |
| |
| script = ( |
| "היי! ברוכים הבאים למורה למתמטיקה. איזה כיף שהצטרפתם אליי! " |
| "אני כאן כדי להפוך את המתמטיקה לחוויה ברורה וחזותית. " |
| "במסך כאן תמצאו שני כפתורים עיקריים. " |
| "לחצו על הכפתור הירוק פתרי לי תרגיל, אם אתם צריכים פתרון מלא ומודרך מאפס. " |
| "או על הכפתור הוורוד בדקי לי תשובה, כדי שאעבור על הדרך שאתם עשיתם ואגיד לכם אם צדקתם. " |
| "חשוב שתדעו, אני תמיד כאן בשבילכם. " |
| "אם סיימתי לפתור ועדיין משהו לא מובן, פשוט תלחצו על הכפתור שאל את המורה. " |
| "אני אשמח להסביר הכל שוב עד שהכל יהיה ברור ומסודר בראש. " |
| "ועוד טיפ קטן ממני. " |
| "אם אתם יושבים ללמוד למבחן, הכי כדאי לעבוד עם מחשב ולא עם הטלפון, כי שם הכל הרבה יותר ברור. " |
| "כדי להתחבר, פשוט נכנסים לאתר מהמחשב ומאשרים את הכניסה דרך הטלפון שלכם. " |
| "שיהיה המון בהצלחה!" |
| ) |
| |
| tts.generate_audio(script, "welcome_speech_v2.wav") |