Release NVIDIA WakeWord Trainer v27

This commit is contained in:
MasterPhooey
2026-08-10 07:06:44 -05:00
parent 68c4227cb7
commit 13f229e451
14 changed files with 572 additions and 262 deletions

View File

@@ -12,6 +12,7 @@ DEFAULT_SAMPLES=50000
DEFAULT_BATCH_SIZE=100
DEFAULT_TRAINING_STEPS=40000
DEFAULT_LANGUAGE=en
DEFAULT_ENGLISH_ACCENT=mixed
DEFAULT_TTS_MODE=hybrid
DEFAULT_TTS_VOICE_COUNT=128
@@ -21,6 +22,7 @@ DEFAULT_TTS_VOICE_COUNT=128
: "${BATCH_SIZE:=${DEFAULT_BATCH_SIZE}}"
: "${TRAINING_STEPS:=${DEFAULT_TRAINING_STEPS}}"
: "${LANGUAGE:=${DEFAULT_LANGUAGE}}"
: "${ENGLISH_ACCENT:=${DEFAULT_ENGLISH_ACCENT}}"
: "${TTS_MODE:=${DEFAULT_TTS_MODE}}"
: "${TTS_VOICE_COUNT:=${DEFAULT_TTS_VOICE_COUNT}}"
: "${CLEANUP_WORK_DIR:=false}"

View File

@@ -31,20 +31,24 @@ if str(ROOT_DIR) not in sys.path:
sys.path.insert(0, str(ROOT_DIR))
from tts_config import ( # noqa: E402
DEFAULT_ENGLISH_ACCENT,
DEFAULT_TTS_MODE,
ENGLISH_ACCENTS,
ENGINE_MOSS,
ENGINE_OMNIVOICE,
ENGINE_PIPER,
ENGINE_QWEN3,
MIXED_ENGLISH_ACCENTS,
QWEN_LANGUAGE_NAMES,
distribute_samples,
engines_for_language,
language_for_engine,
normalize_english_accent,
normalize_tts_mode,
)
GENERATOR_VERSION = "modern-tts-v16-four-provider-direct-corpus-safe-limits"
GENERATOR_VERSION = "modern-tts-v17-four-provider-direct-corpus-safe-limits-english-accent-emphasis"
VOICE_BANK_VERSION = "modern-tts-voice-bank-v1-native-random-qualified-single-utterance"
COMPATIBLE_VOICE_BANK_VERSIONS = {
VOICE_BANK_VERSION,
@@ -199,7 +203,11 @@ def stable_prompt_text(phrase: str, language: str = "en") -> str:
return clean + "."
def qwen_descriptions(language_name: str, count: int) -> list[str]:
def qwen_descriptions(
language_name: str,
count: int,
english_accent: str = DEFAULT_ENGLISH_ACCENT,
) -> list[str]:
genders = ("female", "male")
ages = ("child", "teenager", "young adult", "middle-aged adult", "elderly adult")
pitches = ("low pitch", "medium pitch", "high pitch")
@@ -215,16 +223,30 @@ def qwen_descriptions(language_name: str, count: int) -> list[str]:
weights = ("light", "balanced", "compact", "full-bodied", "resonant")
combinations = list(product(genders, ages, pitches, deliveries, textures, paces, weights))
descriptions = []
accent_cycle: tuple[str, ...] = ()
if language_name == "English":
selected_accent = normalize_english_accent(english_accent, "en")
accent_cycle = (
MIXED_ENGLISH_ACCENTS
if selected_accent == DEFAULT_ENGLISH_ACCENT
else (selected_accent,)
)
# Walking the Cartesian product sequentially clusters the leading traits
# (the first 375 combinations are all female). A coprime stride retains a
# deterministic, non-repeating order while balancing every trait early.
for index in range(count):
combination_index = (index * VOICE_PROFILE_STRIDE) % len(combinations)
gender, age, pitch, delivery, texture, pace, weight = combinations[combination_index]
language_style = f"native {language_name}"
if accent_cycle:
selected_accent = accent_cycle[index % len(accent_cycle)]
language_style = (
f"English with a natural {ENGLISH_ACCENTS[selected_accent]} accent"
)
descriptions.append(
f"A distinct {age} {gender} speaker with a {texture} timbre, "
f"{pitch}, {weight} vocal weight, and {delivery}, speaking native "
f"{language_name} at a {pace} pace. Say only the supplied text once."
f"{pitch}, {weight} vocal weight, and {delivery}, speaking "
f"{language_style} at a {pace} pace. Say only the supplied text once."
)
return descriptions
@@ -283,6 +305,10 @@ def valid_sample(path: Path) -> bool:
class Generator:
def __init__(self, args: argparse.Namespace):
self.args = args
self.english_accent = normalize_english_accent(
getattr(args, "english_accent", DEFAULT_ENGLISH_ACCENT),
args.language,
)
self.spoken_phrase = args.phrase.replace("_", " ").strip()
self.data_dir = args.data_dir.resolve()
self.output_dir = args.output_dir.resolve()
@@ -339,6 +365,7 @@ class Generator:
"generator_version": GENERATOR_VERSION,
"phrase": self.args.phrase,
"language": self.args.language,
"english_accent": self.english_accent,
"tts_mode": self.args.tts_mode,
"samples": self.args.samples,
"engines": engines,
@@ -1064,7 +1091,11 @@ class Generator:
self.direct_attempt[engine] += count
rng = random.Random(24051984 + start + sum(ord(ch) for ch in engine + prefix))
descriptions = (
qwen_descriptions(QWEN_LANGUAGE_NAMES[self.args.language], start + count)[start:]
qwen_descriptions(
QWEN_LANGUAGE_NAMES[self.args.language],
start + count,
self.english_accent,
)[start:]
if engine == ENGINE_QWEN3
else []
)
@@ -1360,6 +1391,8 @@ class Generator:
self.final_dir.mkdir(parents=True, exist_ok=True)
plan = distribute_samples(self.args.samples, engines)
log(f"===== Direct TTS corpus plan ({self.args.tts_mode}, {self.args.language}) =====")
if self.args.language == "en" and ENGINE_QWEN3 in plan:
log(f" English accent emphasis: {self.english_accent}")
for engine, count in plan.items():
log(f" {engine}: {count} sample(s)")
log(
@@ -1447,6 +1480,7 @@ class Generator:
"reusable_profile_bank": False,
"moss_unique_accepted_carriers": True,
"piper_all_model_speakers": True,
"english_accent_emphasis": self.english_accent,
},
"qa": {
"audio_format": "16 kHz mono PCM16 WAV",
@@ -1475,6 +1509,10 @@ def parser() -> argparse.ArgumentParser:
result = argparse.ArgumentParser()
result.add_argument("phrase")
result.add_argument("--language", default="en")
result.add_argument(
"--english-accent",
default=os.environ.get("MWW_ENGLISH_ACCENT", DEFAULT_ENGLISH_ACCENT),
)
result.add_argument("--tts-mode", default=DEFAULT_TTS_MODE)
result.add_argument("--samples", type=int, default=50000)
result.add_argument("--batch-size", type=int, default=8)
@@ -1494,6 +1532,7 @@ def parser() -> argparse.ArgumentParser:
def main() -> int:
args = parser().parse_args()
args.language = args.language.strip().lower().replace("-", "_")
args.english_accent = normalize_english_accent(args.english_accent, args.language)
args.tts_mode = normalize_tts_mode(args.tts_mode)
if args.samples < 1:
raise SystemExit("--samples must be positive")

View File

@@ -4,7 +4,7 @@ set -euo pipefail
PROGPATH="$(realpath "$0")"
PROGDIR="$(dirname "${PROGPATH}")"
KNOWN_ARGS=( samples batch-size data-dir language tts-mode tts-voice-count )
KNOWN_ARGS=( samples batch-size data-dir language english-accent tts-mode tts-voice-count )
# shellcheck source=/dev/null
source "${PROGDIR}/shell.functions"
WAKE_WORD="${POSITIONAL_ARGS[0]:-}"
@@ -17,12 +17,14 @@ fi
if [ "${HELP}" == "true" ] || [ -z "${WAKE_WORD}" ] ; then
cat <<EOF >&2
Usage: $0 [ --samples=<samples> ] [ --batch-size=<batch_size> ]
[ --language=<lang> ] [ --tts-mode=<modern|hybrid|piper> ]
[ --language=<lang> ] [ --english-accent=<accent> ]
[ --tts-mode=<modern|hybrid|piper> ]
[ --tts-voice-count=<voices> ] <wake_word>
--samples: Number of samples to generate. Default: ${DEFAULT_SAMPLES}
--batch-size: Generation batch size. Default: ${DEFAULT_BATCH_SIZE}
--language: TTS language code. Default: ${DEFAULT_LANGUAGE}
--english-accent: English accent emphasis. Default: ${DEFAULT_ENGLISH_ACCENT}
--tts-mode: modern, hybrid, or piper. Default: ${DEFAULT_TTS_MODE}
--tts-voice-count: Deprecated compatibility option; direct generation ignores it.
<wake_word> Required phrase to synthesize.
@@ -38,17 +40,31 @@ case "${TTS_MODE}" in
;;
esac
LANGUAGE="$(echo "${LANGUAGE}" | tr '[:upper:]' '[:lower:]')"
ENGLISH_ACCENT="$(echo "${ENGLISH_ACCENT}" | tr '[:upper:] -' '[:lower:]__')"
if [ "${LANGUAGE}" != "en" ]; then
ENGLISH_ACCENT="mixed"
fi
case "${ENGLISH_ACCENT}" in
mixed|australian|american|british|canadian|irish|scottish|new_zealand|indian|south_african) ;;
*)
echo "ERROR: unsupported --english-accent '${ENGLISH_ACCENT}'." >&2
exit 2
;;
esac
WORK_DIR="${DATA_DIR}/work"
SAMPLES_DIR="${WORK_DIR}/wake_word_samples"
mkdir -p "${WORK_DIR}"
START_TS=$EPOCHSECONDS
echo "===== Generating ${SAMPLES} wake-word samples (language=${LANGUAGE}, tts=${TTS_MODE}) ====="
echo "===== Generating ${SAMPLES} wake-word samples (language=${LANGUAGE}, accent=${ENGLISH_ACCENT}, tts=${TTS_MODE}) ====="
python3 "${PROGDIR}/tts_generate_samples.py" "${WAKE_WORD}" \
--samples="${SAMPLES}" \
--batch-size="${BATCH_SIZE}" \
--language="${LANGUAGE}" \
--english-accent="${ENGLISH_ACCENT}" \
--tts-mode="${TTS_MODE}" \
--voice-count="${TTS_VOICE_COUNT}" \
--data-dir="${DATA_DIR}" \