Добавить в корзинуПозвонить
Найти в Дзене
Аналитика данных

Генерация речи из текста разными голосами на Python: гайд по edge-tts + librosa

Как сгенерировать речь по тексту и желательно бесплатно? Без API-ключей. С возможностью «накрутить» мультяшные, монструозные или роботизированные интонации на разных языках. Ранее решал задачу по переводу записи звонка в текст. А теперь обратная задача. И вот готовый код для запуска в Google Colab. ⚠ Код работает в Google Colab или любом Jupyter-окружении. !pip install -q edge-tts librosa soundfile numpy
import asyncio
import edge_tts
import librosa
import soundfile as sf
from IPython.display import Audio, display
import os # 📝 ТЕКСТ
TEXT = "Я люблю свои дашборды, запущу пайплайны бодро, фильтры все расставлю чётко, покажу начальству ловко!" Сетевые вызовы к бесплатным API иногда падают. Поэтому оборачиваем синтез в асинхронную функцию с повторными попытками: # 🔊 Загрузка аудио
FALLBACK_VOICE = "en-US-GuyNeural" # Запасной голос из Microsoft """Загружает аудио с повторными попытками. При провале возвращает None."""
async def fetch_tts(text, voice, rate, max_retries=2):
····for attem
Оглавление

Как сгенерировать речь по тексту и желательно бесплатно? Без API-ключей. С возможностью «накрутить» мультяшные, монструозные или роботизированные интонации на разных языках.

Ранее решал задачу по переводу записи звонка в текст. А теперь обратная задача. И вот готовый код для запуска в Google Colab.

⚠ Код работает в Google Colab или любом Jupyter-окружении.

Ставим библиотеки

!pip install -q edge-tts librosa soundfile numpy
import asyncio
import edge_tts
import librosa
import soundfile as sf
from IPython.display import Audio, display
import os

Готовим текст на озвучку

# 📝 ТЕКСТ
TEXT = "Я люблю свои дашборды, запущу пайплайны бодро, фильтры все расставлю чётко, покажу начальству ловко!"

Функция асинхронной загрузки с повторами

Сетевые вызовы к бесплатным API иногда падают. Поэтому оборачиваем синтез в асинхронную функцию с повторными попытками:

# 🔊 Загрузка аудио
FALLBACK_VOICE = "en-US-GuyNeural" # Запасной голос из Microsoft
"""Загружает аудио с повторными попытками. При провале возвращает None."""
async def fetch_tts(text, voice, rate, max_retries=2):
····for attempt in range(max_retries):
········try:
············comm = edge_tts.Communicate(text, voice, rate=rate)
············audio_bytes = b""
············async for chunk in comm.stream():
················if chunk["type"] == "audio":
····················audio_bytes += chunk["data"]
············if not audio_bytes:
················raise ValueError("Пустой ответ от TTS")
············return audio_bytes
········except Exception as e:
············if attempt < max_retries - 1:
················await asyncio.sleep(2) # Пауза перед повтором
············else:
················return None # Сигнал о провале

Функция генерации и обработки. При ошибке автоматически переключается на запасной голос

async def generate_cartoon_voice(name, text, voice, pitch, speed, rate):
····audio_bytes = await fetch_tts(text, voice, rate)

····# Если основной голос не ответил, пробуем fallback
····if audio_bytes is None:
········print(f" ⚠️ {voice} недоступен. Использую запасной: {FALLBACK_VOICE}")
········audio_bytes = await fetch_tts(text, FALLBACK_VOICE, rate)
········if audio_bytes is None:
············raise RuntimeError("Не удалось получить аудио даже с запасным голосом")

····# Сохраняем временный MP3
····with open("_temp.mp3", "wb") as f:
········f.write(audio_bytes)

····# Декодирование и эффекты
····y, sr = librosa.load("_temp.mp3", sr=None)
····if pitch != 0:
········y = librosa.effects.pitch_shift(y, sr=sr, n_steps=pitch)
····if speed != 1.0:
········y = librosa.effects.time_stretch(y, rate=speed)

····out_path = f"{name}.wav"
····sf.write(out_path, y, sr)
····return out_path

Параметры звука

pitch — высота тона, меняет частоту звука, не ускоряя и не замедляя речь

  • Единицы: полутона музыкальной шкалы. +12 = ровно на 1 октаву выше,
  • -12 = на 1 октаву ниже.

speed — скорость воспроизведения

  • Коэффициент: 1.0 = оригинал, 1.25 = аудио ускоряется на 25%.

rate — скорость синтеза в edge-tts. Это параметр ИИ: указывается строкой "+10%", "-20%" и т.д. "+10%" → нейросеть Microsoft говорит чуть быстрее, сокращает паузы, делает артикуляцию более чёткой и «суетливой».

Влияет на дыхание, связки между слогами и естественность ещё до наложения цифровых эффектов. Это не просто ускорение плёнки, а изменение того, как ИИ произносит текст.

Пресеты голоса

PRESETS = {
"👵_Добрая_бабушка": {
"voice": "ru-RU-SvetlanaNeural", "pitch": +3, "speed": 1.00, "rate": "-15%"
},
"👹_Монстр": {
"voice": "ru-RU-DmitryNeural", "pitch": -10, "speed": 0.70, "rate": "-25%"
},
"🤖_Робот":{
"voice": "ru-RU-DmitryNeural", "pitch": -2, "speed": 1.10, "rate": "+5%"
}}

Полный список голосов можно посмотреть на сайте Microsoft: Language and voice support for Azure Speech в разделе Language and voice support.

-2

Функция генерации аудио

print("🔊 Генерация аудио (с авто-повторами при ошибках)...\n")
for title, p in PRESETS.items():
····try:
········path = await generate_cartoon_voice(
············name=title, text=TEXT,
············voice=p["voice"], pitch=p["pitch"],
············speed=p["speed"], rate=p["rate"]
········)
········print(f"✅ {title}: {path}")
····except Exception as e:
········print(f"❌ {title}: {e}")
# Очистка
if os.path.exists("_temp.mp3"):
····os.remove("_temp.mp3")
print("\n🎧 Аудиоплеер:")
for title in PRESETS.keys():
····if os.path.exists(f"{title}.wav"):
········print(f"\n🎙️ Персонаж: {title.replace('_', ' ')}")
········display(Audio(f"{title}.wav", autoplay=False))
Вывод сгенерированных аудио-файлов прямо в ноутбуке
Вывод сгенерированных аудио-файлов прямо в ноутбуке

Скачивание wav-файлов в ZIP-архиве

!zip cartoon_voices.zip *.wavfrom google.colab import filesfiles.download('cartoon_voices.zip')
Сохранение файлов
Сохранение файлов

Также скачать полученные файлы можно из кеша Google Colab пока запущен сеанс ноутбука.

Папка Google Colab
Папка Google Colab

Весь код на GitHub-e 😸