Свои звуки и голос
Пошагово: как заменить звуки, которые идут в поставке, на свои. Ничего из этого не обязательно для первого запуска — проект звучит из коробки. Это нужно, когда персонаж должен звучать своим голосом, а не стандартным.
Что вообще звучит
В проекте два независимых набора звука, и путать их не стоит.
Голосовые референсы персонажа — resources/Characters/<персонаж>/Voice/.
Это не то, что персонаж говорит. Это образцы, по которым синтезатор понимает,
каким голосом и с какой интонацией произносить любой новый текст. На каждую
эмоцию свой образец.
Звуки интерфейса — resources/Audio/fx/. Короткие сигналы событий:
включение, ошибка, бан слова, запись в базу, критический отказ. Персонаж
проигрывает их сам, у агента есть инструмент для этого.
Всё, что лежит в репозитории, сделано самим проектом: сигналы синтезированы скриптом, реплики диктора — синтезом речи. Чужой звук сюда класть нельзя — вырезанное из игр, фильмов и роликов не покрывается лицензией проекта, даже если положить «на время» и никому не показывать.
Требования к файлам
Одинаковые для обоих наборов:
| Параметр | Значение |
|---|---|
| Формат | WAV, PCM 16 бит |
| Каналы | моно |
| Частота | 44 100 Гц |
Проверить, что у файла внутри:
ffprobe -v error -show_entries stream=channels,sample_rate,codec_name -of default=nw=1 mysound.wav
Привести к нужному виду что угодно — mp3, стерео, другая частота:
ffmpeg -i исходник.mp3 -ac 1 -ar 44100 -c:a pcm_s16le результат.wav
Голос персонажа
Шаг 1. Заведите своего персонажа
Не правьте Worshiper — скопируйте его и работайте с копией, тогда эталон
останется целым:
cp -r resources/Characters/Worshiper resources/Characters/MyChar
В .env укажите:
NETTYAN_CHARACTER_NAME="MyChar"
Шаг 2. Запишите образцы
На каждую эмоцию — один фрагмент. Что работает:
- длительность 3–10 секунд; короче двух секунд результат нестабилен, длиннее десяти ничего не улучшает
- один говорящий, без музыки, без фоновых голосов, без эха
- ровная громкость, без клиппинга
- интонация должна соответствовать эмоции: образец для
happyпроизносится радостно, иначе синтез не поймёт, чего от него хотят
Записать можно чем угодно — микрофоном, любым синтезатором речи, своим же
simple_tts_server.py.
Шаг 3. Разложите файлы
resources/Characters/MyChar/Voice/
├── neutral.wav
├── happy.wav
├── sad.wav
└── refs_manifest.yml
Шаг 4. Опишите их в манифесте
refs_manifest.yml связывает эмоцию, файл и точную расшифровку:
neutral:
wav: neutral # имя файла без .wav
text: Чем могу быть полезен? # ровно то, что в файле произнесено
happy:
wav: happy
text: Отличные новости, у нас всё получилось!
Эмоций может быть сколько угодно, добавляются просто новыми блоками.
Обязательна одна — neutral: если агент выдаст эмоцию, которой в манифесте
нет, подставится она.
Звуки интерфейса
Вариант А. Пересобрать генератором
Сигналы из alarms/ не записаны, а синтезированы. Проще всего поправить
генератор и пересобрать:
python tools/make_fx_sounds.py --list # посмотреть, что создаётся
python tools/make_fx_sounds.py # пересоздать
Зависимостей не нужно, только стандартная библиотека. Внутри функции build()
каждый сигнал собирается из примитивов:
| Примитив | Что делает |
|---|---|
tone(freq, sec, vol, freq_end=) |
тон; с freq_end — плавный сдвиг частоты |
noise(sec, vol) |
шум |
envelope(s, attack, release) |
сглаживает края, без него слышен щелчок |
decay(s, power) |
затухание — так звучат колокол и щелчки |
mix(a, b, ...) |
наложить друг на друга |
seq(a, b, ...) |
проиграть подряд |
silence(sec) |
пауза |
Например, сигнал готовности — две восходящие ноты:
s["alarms/ready.wav"] = seq(
envelope(tone(660, 0.13, 0.42), 0.005, 0.03),
envelope(tone(990, 0.22, 0.42), 0.005, 0.08))
Вариант Б. Положить свои файлы
- Приведите к WAV моно 44.1 кГц 16 бит (команда выше).
- Положите в
resources/Audio/fx/alarms/илиresources/Audio/fx/records/. - Пропишите путь в
fx_manifest.ymlнапротив имени события.
Держите сигналы короткими: всё длиннее двух секунд персонаж будет перебивать собственной речью.
Как читается fx_manifest.yml
Одиночный сигнал — имя события и путь:
error: records/error.wav
bell: alarms/bell.wav
Последовательность — несколько сигналов подряд по именам:
starting:
- poweron
- none # короткая тишина, служит паузой
- powerstarting
Внутри последовательности есть два особых элемента:
wiped_state:
- sequence: fatal_errored # вложенная последовательность
delay: 4 # пауза перед ней, секунды
wait: True # дождаться конца, а не запускать параллельно
- bell
Ключ none удалять нельзя — он используется как пауза.
Если что-то пошло не так
Звука нет вообще, но и ошибок нет. Файл, указанный в манифесте, не найден на
диске — воспроизведение в таком случае молча пропускается. Сверьте путь: он
считается от папки resources/Audio/fx/, а в refs_manifest.yml имя пишется
без расширения .wav.
Звук играет не туда. Проверьте FX_SOUND_DEVICE_IN и FX_SOUND_DEVICE_OUT
в .env — там должно стоять полное название устройства виртуального
аудиокабеля. Осторожно с направлениями: OUTPUT ведёт себя как микрофон,
а INPUT как колонки.
Клонированный голос звучит криво, будто не тот человек. Почти всегда
расшифровка в text не совпадает с тем, что реально произнесено в файле.
Сверьте дословно, включая знаки препинания.
Голос дрожит или обрывается. Образец слишком короткий либо шумный. Возьмите фрагмент на 3–10 секунд с чистой записью.
На стыке сигналов слышен щелчок. Файл начинается или кончается резко.
Прогоните через envelope() в генераторе или добавьте в редакторе короткое
затухание в начале и конце.
Персонаж перебивает сам себя. Сигнал слишком длинный. Режьте до двух секунд.