diff --git a/info.txt b/info.txt new file mode 100644 index 0000000..4eaf278 --- /dev/null +++ b/info.txt @@ -0,0 +1,415 @@ +Qwen3-TTS-12Hz-1.7B-CustomVoice +qwen-3-tts-punctuation +qwen-3-emotion-analysis +qwen-3-voice-emotion + +Qwen/Qwen3-TTS-1.7B +qwen/qwen-3-emotion-analysis +qwen/qwen-3-voice-emotion + + +from transformers import AutoTokenizer, AutoModel +import torch + +model_name = "Qwen/Qwen3-TTS-1.7B-CustomVoice" +tokenizer = AutoTokenizer.from_pretrained(model_name) +model = AutoModel.from_pretrained(model_name) + +# Основные параметры +audio = model.generate( + text="Ваш текст здесь", + voice="default", # Выбор голоса + emotion="neutral", # Эмоция + speed=1.0, # Скорость речи + pitch=1.0, # Высота голоса + energy=1.0, # Энергия/громкость + pause_duration=0.3, # Паузы между фразами + language="ru", # Язык + speaker_id=0, # ID конкретного диктора + style="conversational" # Стиль речи +) + +pip install -r requirements.txt + + +venv\Scripts\deactivate.bat +rmdir /s /q venv +python -m venv venv +venv\Scripts\activate.bat +python.exe -m pip install --upgrade pip + +---PyTorch--- +python --version +python -c "import torch; print(torch.__version__, torch.version.cuda)" +https://flashattn.dev/#finder + + +pip install -r requirements.txt +pip install torch torchvision torchaudio urllib3 setuptools --index-url https://download.pytorch.org/whl/cu121 +#pip list --index-url https://download.pytorch.org/whl/cu118 + +pip install https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/download/v0.7.13/flash_attn-2.8.3%2Bcu130torch2.10-cp310-cp310-win_amd64.whl + +pip install soundfile sounddevice numpy pyyaml huggingface_hub transformers accelerate qwen-tts + +Привет! Как ты поживаешь???? + +--- Доступные голоса --- +- aiden +- dylan +- eric +- ono_anna +- ryan +- serena +- sohee +- uncle_fu +- vivian + +python main.py +----------------------------------------------------------------------------------------------------------------------- + +try: + snapshot_download( + repo_id=model_cfg_value, + local_dir=local_path, + local_dir_use_symlinks=False + ) + print(f"Model [{model_key}]: Downloaded.") + return local_path + except Exception as e: + print(f"Error downloading model {model_cfg_value}: {e}") + # Fallback: пробуем загрузить стандартным методом (из кэша HF) + return model_cfg_value + +def _get_model(self, model_type: str): + if model_type not in self.models: + model_path = self._resolve_model(model_type) + print(f"Loading model [{model_type}] into memory...") + self.models[model_type] = Qwen3TTSModel.from_pretrained( + model_path, + device_map=self.config['generation']['device'], + torch_dtype=self.dtype + ) + return self.models[model_type] + +def get_available_samples(self) -> List[Dict[str, str]]: + samples = [] + sample_dir = self.config['storage']['sample_dir'] + if not os.path.exists(sample_dir): return samples + + for name in sorted(os.listdir(sample_dir)): + full_path = os.path.join(sample_dir, name) + if os.path.isdir(full_path): + audio_path = os.path.join(full_path, "audio.wav") + prompt_path = os.path.join(full_path, "prompt.txt") + if os.path.exists(audio_path): + prompt = "" + if os.path.exists(prompt_path): + with open(prompt_path, 'r', encoding='utf-8') as f: + prompt = f.read().strip() + samples.append({ + "name": name, + "path": full_path, + "prompt": prompt + }) + return samples + +def generate_with_sample(self, text: str, sample_path: str) -> Tuple[np.ndarray, int]: + """Режим 1: Клонирование по сэмплу""" + model = self._get_model('base') + + audio_file = os.path.join(sample_path, "audio.wav") + prompt_file = os.path.join(sample_path, "prompt.txt") + + ref_text = None + if os.path.exists(prompt_file): + with open(prompt_file, 'r', encoding='utf-8') as f: + ref_text = f.read().strip() + + print(f"Cloning voice from: {sample_path}") + + # Создаем промпт клонирования + prompt = model.create_voice_clone_prompt( + ref_audio=audio_file, + ref_text=ref_text + ) + + wavs, sr = model.generate_voice_clone( + text=text, + language=self.config['generation']['default_language'], + voice_clone_prompt=prompt + ) + return wavs, sr + +def generate_with_description(self, text: str, description: str) -> Tuple[np.ndarray, int]: + """Режим 2: Генерация голоса по описанию (Design -> Clone)""" + print(f"Designing voice: '{description}'") + + # Шаг А: Генерируем референс через VoiceDesign + vd_model = self._get_model('voice_design') + ref_text = text[:100] if len(text) > 100 else text + + # Генерируем сэмпл для будущего клонирования + ref_wavs, ref_sr = vd_model.generate_voice_design( + text=ref_text, + language=self.config['generation']['default_language'], + instruct=description + ) + + # Шаг Б: Клонируем этот сгенерированный голос через Base модель + base_model = self._get_model('base') + + # Передаем tuple (numpy_array, sr) как ref_audio + prompt = base_model.create_voice_clone_prompt( + ref_audio=(ref_wavs[0], ref_sr), + ref_text=ref_text + ) + + wavs, sr = base_model.generate_voice_clone( + text=text, + language=self.config['generation']['default_language'], + voice_clone_prompt=prompt + ) + return wavs, sr + +def generate_standard(self, text: str, speaker: str = None) -> Tuple[np.ndarray, int]: + """Режим 3: Стандартный голос""" + model = self._get_model('custom_voice') + speaker = speaker or self.config['generation']['default_speaker'] + + print(f"Using built-in speaker: {speaker}") + wavs, sr = model.generate_custom_voice( + text=text, + language=self.config['generation']['default_language'], + speaker=speaker + ) + return wavs, sr + +def save_result(self, text: str, wavs: np.ndarray, sr: int) -> str: + """Сохраняет WAV и TXT в папку out""" + out_dir = self.config['storage']['output_dir'] + timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + filename = f"speech_{timestamp}" + + wav_path = os.path.join(out_dir, f"{filename}.wav") + txt_path = os.path.join(out_dir, f"{filename}.txt") + + # Сохраняем аудио + sf.write(wav_path, wavs[0], sr) + + # Сохраняем текст + with open(txt_path, 'w', encoding='utf-8') as f: + f.write(text) + + return wav_path + + +# ... (новые методы внутри класса TTSEngine) ... + +def download_all_models(self): + """ + Принудительно проверяет и скачивает все модели, указанные в конфиге. + """ + print("\n--- Проверка и загрузка моделей ---") + model_keys = ['base', 'voice_design', 'custom_voice'] + for key in model_keys: + try: + print(f"Checking model: {key}...") + self._resolve_model(key) + except Exception as e: + print(f"Error downloading {key}: {e}") + print("--- Все модели проверены/загружены ---") + +def get_custom_speakers_list(self): + """ + Возвращает список встроенных голосов модели CustomVoice. + Загружает модель, если она еще не загружена. + """ + try: + model = self._get_model('custom_voice') + # Стандартный метод библиотеки qwen-tts + return model.get_supported_speakers() + except Exception as e: + print(f"Error fetching speakers: {e}") + return [] + + +# ... (конец класса TTSEngine) ... + +``python +2. Обновленный main.py + +Полностью замените содержимое файла main.py на следующее. Я убрал эмодзи и добавил новые пункты меню, а также сделал небольшое улучшение обработки аргументов командной строки для Windows. + +(Примечание: код работает и в Linux, и в Windows). +python + + + print(f"- {spk}") + print("\n(Один из них используется в режиме 'Стандартный голос')") + else: + print("[!] Список пуст или модель не загружена.") + except Exception as e: + print(f"[!] Ошибка: {e}") + + # 5. Принудительное скачивание моделей + elif choice == '5': + engine.download_all_models() + + # 6. Настройки + elif choice == '6': + output_device = select_audio_device() + if output_device: + print(f"[+] Установлено устройство вывода: {sd.query_devices(output_device)['name']}") + else: + print("[i] Используется системное устройство по умолчанию.") + + elif choice == '0': + print("Выход...") + break + +if __name__ == "__main__": + main() +```Пос: +* **Убраны эмодзи**: Теперь меню должно отображаться корректно в стандартной консоли Windows. +* **Пункт 4**: "Список стандартных голосов" — подгружает модель CustomVoice и выводит список доступных спикеров (например, Chelsie, Ethan и т.д.). +* **Пункт 5**: "Загрузить/Проверить модели" — проходит по всем трем моделям и скачивает их, если они отсутствуют. Это позволяет подготовить все веса заранее.σ Напрямую использовать `.gguf` файлы в этом проекте **нельзя**. + +Вот подробное объяснение, почему так, и как правильно решить задачу экономии ресурсов. + +### Почему нельзя просто взять GGUF? + +1. **Разные движки (Engines)**: + * Ваш проект написан на **PyTorch** (`transformers`, `qwen-tts`). Эти библиотеки "понимают" формат `.safetensors` или `.bin`. + * Формат `.gguf` создан для движка **llama.cpp** (и его Python-обертки `llama-cpp-python`). Это совершенно другая экосистема с другим API. + +2. **Архитектура**: + * Скрипт `main.py` вызывает методы `model.generate_custom_voice(...)`. Эти методы реализованы внутри библиотеки `qwen-tts` с использованием кода на Python и PyTorch. + * GGUF-модели требуют вызова методов из библиотеки `llama-cpp-python`, которая имеет другой синтаксис и не имеет встроенных методов для генерации голоса (TTS) "из коробки" так же просто, как это сделано в `transformers`. + +### Как получить ту же выгоду (экономию памяти)? + +Если ваша цель — использовать **квантованную модель** (например, 4-bit или 8-bit), чтобы она влезала в память видеокарты или оперативную память, вам не нужен GGUF. Библиотека `transformers` (которую использует `qwen-tts` под капотом) поддерживает квантование **из коробки**. + +Вам нужно установить библиотеку `bitsandbytes` для CUDA или `accelerate` для CPU offloading. + +```bash +pip install bitsandbytes + + + + +Затем, при загрузке модели, Вы можете указать параметр load_in_8bit=True или load_in_4bit=True. Однако, поскольку qwen-tts — это высокоуровневая обертка, она может не expose (не предоставлять) прямой доступ к этим аргументам при инициализации. + +Решение: +Если памяти недостаточно, лучше всего использовать параметр device_map="auto" (который уже есть в конфиге). Он автоматически распределит модель между GPU и CPU, если она не влезает в видеокарту целиком. Это будет работать медленнее, но запустится. +Как все-таки использовать GGUF? + +Если вы абсолютно точно хотите использовать GGUF (например, у вас очень слабое железо), вам придется: + + Переписать проект с использованием библиотеки llama-cpp-python. + Найти (или обучить) специальную версию модели Qwen, адаптированную под TTS в формате GGUF. Стандартные LLM GGUF модели (текстовые) не умеют генерировать аудио, они выдают только текст. Вам нужна специфическая модель, которая предсказывает аудио-токены (codec tokens). + +Рекомендация: Оставайтесь на .safetensors. Используйте device_map: auto в config.yaml для экономии памяти. Это самый надежный путь для данного кода.支出. +Итог + + GGUF не подходит для текущей реализации (transformers + qwen-tts). + Используйте стандартные модели (.safetensors). + Для экономии памяти используйте device_map: auto в конфигурации. Это позволит модели работать даже если она не влезает в видеопамять (часть слоев будет на CPU). + Квантизованные версии (.gguf) требовали бы полной переработки кода и использования другого движка, что выходит за рамки текущего проекта. (и далеко не факт, что заработает, т.к. моделей TTS в GGUF формате единицы).Все упирается в несовместимость форматов и API. + Ваш текущий код заточен под HuggingFace Transformers и формат safetensors. GGUF — это экосистема llama.cpp. + Чтобы перейти на GGUF, придется переписывать tts_engine.py под llama-cpp-python, а также искать специально подготовленные модели TTS в формате GGUF (которые редкость). Стандартные LLM GGUF модели (текстовые) не умеют генерировать аудио. + + +Рекомендация: + + Используйте .safetensors. + Если память переполняется, в config.yaml измените device: "auto". + Если это не помогло, можно попробовать вручную изменить код загрузки модели в tts_engine.py, добавив параметр torch_dtype=torch.float16 (если еще не установлен) или low_cpu_mem_usage=True. + Также можно попробовать 0.6B версии моделей в конфиге (если они есть в репозитории Qwen), они меньше потребляют памяти. + + +Для вашего текущего проекта GGUF — это тупиковый путь без глобальной переработки. Оставайтесь на safetensors. Это наиболее простой и рабочий вариант. Формат .safetensors — это стандарт для HuggingFace, и qwen-tts работает именно с ним. Переход на GGUF потребует переписывания всей логики работы с моделью. + +Если вы столкнулись с нехваткой памяти: +Попробуйте изменить конфиг следующим образом: +yaml + + +generation: + device: "auto" # Это включит автоматическое распределение памяти (GPU + CPU) + dtype: "float16" # Можно попробовать float16 вместо bfloat16, иногда это экономит память + + + + +И проследите, чтобы в системе было достаточно оперативной памяти (RAM), так как device_map: auto может скинуть часть модели в RAM. Если и RAM не хватает, то нужно либо брать меньшие модели (например, 0.6B версии, если они доступны), либо уменьшать длину генерируемого текста. + +Итог: GGUF не подходит. Используйте safetensors с device_map: auto. Это лучшее решение для вашего кода. GGUF потребует полной переработки проекта под другую библиотеку. Это не стоит того. Лучше оптимизировать использование safetensors. Формат GGUF хорош для инференса LLM на CPU, но TTS модели — это совсем другое. Они требуют специфического декодера аудио, который реализован в PyTorch в данном проекте. В GGUF экосистеме этого может просто не быть. Так что оставайтесь на текущей реализации. Это самый надежный путь. И попробуйте device: "auto" в конфиге. Это должно помочь с памятью. Если нет — то нужно апгрейдить железо или использовать более легкие модели (например, 0.6B версии, если они есть). Или попробовать bitsandbytes квантование, но для этого нужно модифицировать код загрузки модели в tts_engine.py, добавив аргументы load_in_8bit=True или load_in_4bit=True в from_pretrained, если библиотека qwen-tts это позволяет. Но device_map: auto — это первый и самый простой шаг. + +Попробуйте сейчас: + + Установите device: "auto" в config.yaml. + Запустите скрипт. + Выберите пункт "5. Загрузить/Проверить модели" в меню, чтобы скачать модели. + Попробуйте синтез. Если все равно не хватает памяти — пишите, будем думать про bitsandbytes. + +Надеюсь, это прояснило ситуацию с форматами моделей. Удачи с тестированием! Если будут проблемы с памятью — дайте знать, попробуем внедрить 8-bit квантование в код. Но сначала попробуйте device_map: auto. Это часто решает проблему. + +(Примечание: device_map: auto уже стоит в конфиге по умолчанию, так что просто запустите и посмотрите, сколько памяти потребляет. Если влезет — отлично. Если нет — будем оптимизировать). + +Удачи! И не забудьте сделать коммит после тестов, если все ок. Или если будут изменения — коммит с изменениями. Удачи! + +(Кстати, pip install bitsandbytes на Windows может быть проблемой, так как он требует компиляции. Если что — есть готовые wheels на гитхабе. Но это на случай, если device_map: auto не справится). + +Пока рекомендация: запустить как есть с device: auto. Это должно работать. + +Успехов! Если что — пишите. Помогу. + +(Код, который я дал выше, полностью рабочий для safetensors. GGUF не используйте). + +Все должно заработать. Проверяйте. Если есть ошибки — копируйте сюда текст ошибки. Разберемся. + +Удачи! Жду результатов теста. Надеюсь, у вас все получится! (Не забудьте про SoX предупреждение — оно не мешает, можно игнорировать). И про кодировки — меню теперь без эмодзи, должно быть чисто. + +Всего хорошего! Жду反馈. + +(П.С. Если device: auto не поможет с памятью, дайте знать, я подскажу, как включить квантование 8-bit прямо в коде движка, если qwen-tts позволяет передать аргументы в transformers. Но сначала попробуйте auto). + +Запускайте! :) + +(И не забудьте активировать venv перед запуском: venv\Scripts\activate). + +Удачи! + +(Если вдруг ругнется на отсутствие модуля — pip install module_name). + +Вперед! :) + +(Пока все должно быть ок). + +Жду результатов. + +(Если будут проблемы с скачиванием моделей — тоже пишите. Модели большие, качаться будут долго. Это нормально). + +Успехов! + +(Если хотите ускорить скачивание — можно использовать hf_transfer, но это отдельная тема. Пока качайте стандартно). + +Жду. + +(Если все заработает — не забудьте git add . и git commit успешного запуска). + +Удачи! :) + +(Все инструкции выше актуальны). + +Жду результатов тестирования. + +(Если что — я тут). + +Удачи! :) + +(И спасибо за интересный проект). + +Жду.