voice_audio_multi/tests/kokoro_tts/validate_multilingual_packages.py
Fabio c36510c21e
Some checks failed
Linux Build / Linux cpu (push) Has been cancelled
Linux Build / Linux cpu-clang (push) Has been cancelled
Linux Build / Linux vulkan (push) Has been cancelled
mac-build / macOS CPU (push) Has been cancelled
Nix Build / Nix (cpu) (push) Has been cancelled
Nix Build / Nix (vulkan) (push) Has been cancelled
windows-build / Windows CPU (push) Has been cancelled
Initial import: audio.cpp with Orange Pi documentation
2026-11-04 19:05:22 +01:00

56 lines
3.1 KiB
Python

"""Synthesize one UTF-8 sample for every Kokoro language from both standalone GGUFs."""
import argparse
import json
from pathlib import Path
import subprocess
import time
import wave
import numpy as np
p = argparse.ArgumentParser()
p.add_argument('--cli', type=Path, required=True)
p.add_argument('--models', type=Path, required=True)
p.add_argument('--output', type=Path, required=True)
p.add_argument('--types', nargs='+', default=['q8_0', 'bf16'])
a = p.parse_args()
cases = json.loads(Path(__file__).with_name('multilingual_cases.json').read_text(encoding='utf-8'))[:7]
cases = [
{'language':'en-us','voice':'af_heart','text':'Hello, this is a native Kokoro TTS test. Good morning to everyone.'},
{'language':'en-gb','voice':'bf_emma','text':'Hello, this is a native Kokoro TTS test. Good morning to everyone.'}
] + cases
codes = {'e':'es', 'f':'fr-fr', 'h':'hi', 'i':'it', 'p':'pt-br', 'j':'ja', 'z':'zh'}
a.output.mkdir(parents=True, exist_ok=True)
report = []
for precision in a.types:
model = a.models.resolve() / ('kokoro-82m-' + precision + '.gguf')
for case in cases:
lang = codes.get(case['language'], case['language'])
dest = a.output.resolve() / (precision + '-' + lang)
dest.mkdir(exist_ok=True)
textfile = dest / 'text.txt'
textfile.write_text(case['text'] + '\n', encoding='utf-8')
wav = a.output.resolve() / (precision + '-' + lang + '.wav')
command = [str(a.cli.resolve()), '--task', 'tts', '--family', 'kokoro_tts',
'--model', str(model), '--backend', 'cpu', '--threads', '8', '--seed', '1234',
'--voice-id', case['voice'], '--language', lang, '--batch-text-file', str(textfile),
'--batch-merge-audio', 'concat', '--out', str(wav),
'--log', '--log-file', str(dest / 'inference.log')]
start = time.perf_counter()
run = subprocess.run(command, capture_output=True, encoding='utf-8', errors='replace', timeout=180)
elapsed = time.perf_counter() - start
(dest / 'console.log').write_text(run.stdout + run.stderr, encoding='utf-8')
result = {'precision':precision, **case, 'exit_code':run.returncode, 'process_seconds':elapsed}
if run.returncode == 0:
with wave.open(str(wav), 'rb') as f:
assert f.getsampwidth() == 2
pcm = np.frombuffer(f.readframes(f.getnframes()), dtype='<i2').astype(np.float64) / 32768
result.update(sample_rate=f.getframerate(), channels=f.getnchannels(),
seconds=f.getnframes()/f.getframerate(), rms=float(np.sqrt(np.mean(pcm * pcm))),
peak=float(np.abs(pcm).max()), file=str(wav))
result['valid_audio'] = result['seconds'] > 1 and result['rms'] > 0.002
else:
result['error'] = (run.stdout + run.stderr)[-2000:]
report.append(result)
(a.output / 'validation.json').write_text(json.dumps(report, ensure_ascii=False, indent=2), encoding='utf-8')
print(precision, lang, 'PASS' if result.get('valid_audio') else result.get('error', 'INVALID AUDIO'), flush=True)
if any(not r.get('valid_audio') for r in report): raise SystemExit(1)