Luigi commited on
Commit
ab0f1bd
·
verified ·
1 Parent(s): f8f98a9

one-click rebuild_voice.sh + generators + text pools

Browse files
Files changed (1) hide show
  1. scripts/rebuild_voice.sh +85 -0
scripts/rebuild_voice.sh ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # ============================================================================
3
+ # ONE-CLICK: build a full zh-TW + English 8 kHz PrimeTTS model in YOUR voice.
4
+ # The reference voice is the ONLY input you change; the text pools, ASR gate,
5
+ # alignment and training recipe are fixed. Both vocoder and acoustic are
6
+ # retrained (both are voice-specific).
7
+ #
8
+ # RUN FROM THE REPO ROOT:
9
+ # PY=/path/to/venv/bin/python ./scripts/rebuild_voice.sh ref.wav ref.txt mytag
10
+ #
11
+ # ref.wav ~6-12 s clean clip of your target voice (mono; zh-TW that also reads
12
+ # a little English is ideal). ref.txt its exact transcript.
13
+ # No recording? synth a reference with Edge-TTS (pip install edge-tts):
14
+ # edge-tts --voice zh-TW-HsiaoYuNeural --text "您好,歡迎來電。Thank you for calling." --write-media ref.mp3
15
+ # ffmpeg -y -i ref.mp3 -ar 24000 -ac 1 ref.wav ; printf '%s' "您好,歡迎來電。Thank you for calling." > ref.txt
16
+ #
17
+ # PREREQS (one venv, on PYTHONPATH=repo root):
18
+ # pip install torch torchaudio transformers onnxruntime soundfile librosa \
19
+ # g2pw g2p_en cn2an opencc inflect faster-whisper edge-tts voxcpm nltk
20
+ # python -c "import nltk; nltk.download('names')"
21
+ # # base warm-start ckpt: download inflect_nano_v1_acoustic.pt from owensong/Inflect-Nano-v1
22
+ # Result corpus: ~12k clips (≈27% zh / 50% mix / 23% en), entity clips ×2 at train time.
23
+ # ============================================================================
24
+ set -e
25
+ REF=${1:?need ref.wav}; REFTXT=${2:?need ref.txt}; TAG=${3:-myvoice}
26
+ PY=${PY:-python} # a venv with all deps above
27
+ GPUS=${GPUS:-0,1}; G0=${GPUS%,*}; G1=${GPUS#*,}
28
+ INIT=${INIT:-inflect_nano_v1_acoustic.pt} # base acoustic (owensong/Inflect-Nano-v1)
29
+ export PYTHONPATH=${PYTHONPATH:-$PWD}
30
+ S=scripts; C=corpus_${TAG}; mkdir -p $C/wav
31
+
32
+ echo "### 1. Text pools (committed in the repo; regenerated only if absent) ###"
33
+ [ -f voxcpm_texts.jsonl ] || { echo "need voxcpm_texts.jsonl (base 2500 zh / 2000 en / 2800 mix)"; exit 1; }
34
+ [ -f codemix_v2.txt ] || $PY $S/gen_codemix_v2.py --n 3000 --exclude codemix_corpus.txt --out codemix_v2.txt
35
+ [ -f entity_texts.jsonl ] || $PY $S/gen_entity_texts.py --n 2600 --out entity_texts.jsonl
36
+ $PY - <<PY
37
+ import json
38
+ with open("$C/all_texts.jsonl","w",encoding="utf-8") as o:
39
+ for l in open("voxcpm_texts.jsonl"): o.write(l)
40
+ for i,t in enumerate(x.strip() for x in open("codemix_v2.txt") if x.strip()):
41
+ o.write(json.dumps({"id":f"mz{i:05d}","text":t,"lang":"mix"},ensure_ascii=False)+"\n")
42
+ for l in open("entity_texts.jsonl"): o.write(l)
43
+ print("assembled", "$C/all_texts.jsonl")
44
+ PY
45
+
46
+ echo "### 2. Clone YOUR voice for every line (VoxCPM2), dual-GPU ###"
47
+ $PY -c "ls=[l for l in open('$C/all_texts.jsonl') if l.strip()];open('$C/t0.jsonl','w').writelines(ls[0::2]);open('$C/t1.jsonl','w').writelines(ls[1::2])"
48
+ CUDA_VISIBLE_DEVICES=$G0 $PY $S/gen_voxcpm_corpus.py --texts $C/t0.jsonl --ref "$REF" --ref-text "$REFTXT" --out-dir $C/wav --manifest $C/m0.jsonl &
49
+ CUDA_VISIBLE_DEVICES=$G1 $PY $S/gen_voxcpm_corpus.py --texts $C/t1.jsonl --ref "$REF" --ref-text "$REFTXT" --out-dir $C/wav --manifest $C/m1.jsonl &
50
+ wait; cat $C/m0.jsonl $C/m1.jsonl > $C/manifest.jsonl
51
+
52
+ echo "### 3. ASR-gate generic clips; trust entity (et*) clips as-is ###"
53
+ $PY -c "
54
+ import json
55
+ g=open('$C/to_gate.jsonl','w'); e=open('$C/entity.jsonl','w')
56
+ for l in open('$C/manifest.jsonl'): (e if json.loads(l)['id'].startswith('et') else g).write(l)"
57
+ CUDA_VISIBLE_DEVICES=$G0 $PY $S/asr_filter.py --manifest $C/to_gate.jsonl --out $C/to_gate --device cuda
58
+ cat $C/to_gate.clean.jsonl $C/entity.jsonl > $C/clean.jsonl
59
+
60
+ echo "### 4. Normalize entities + phone-level align + entity-upsample 2x ###"
61
+ $PY $S/build_corpus_v3.py --out $C/norm.jsonl $C/clean.jsonl
62
+ CUDA_VISIBLE_DEVICES=$G0 $PY $S/align_durations_v4.py --manifest $C/norm.jsonl --out $C/align.jsonl --device cuda
63
+ $PY -c "import json;r=[l for l in open('$C/align.jsonl') if l.strip()];e=[l for l in r if json.loads(l).get('id','').startswith('et')];open('$C/align.up.jsonl','w').writelines(r+e);print('train rows',len(r)+len(e))"
64
+
65
+ echo "### 5. Train 8 kHz vocoder on your voice ###"
66
+ $PY -c "import json;o=open('$C/voc_rows.jsonl','w');[o.write(json.dumps({'target_audio':json.loads(l)['target_audio'],'target_text':json.loads(l)['text']},ensure_ascii=False)+'\n') for l in open('$C/norm.jsonl')]"
67
+ CUDA_VISIBLE_DEVICES=$G1 $PY -m inflect_nano.vocoder --train-jsonl $C/voc_rows.jsonl --out-dir $C/vocoder_8k \
68
+ --variant snake_8k --steps 40000 --batch-size 16 --segment-size 16384 --stft-weight 2.0 --save-interval 5000 --device cuda
69
+
70
+ echo "### 6. Train acoustic: warm-start v1 + 2D mel-GAN + en-upsample 2 ###"
71
+ CUDA_VISIBLE_DEVICES=$G0 $PY -m inflect_nano.acoustic --durations-jsonl $C/align.up.jsonl --out-dir $C/acoustic_8k \
72
+ --vocoder-variant snake_8k --sample-rate 8000 --vocoder-checkpoint $C/vocoder_8k/hifigan-snake_8k-final.pt \
73
+ --vocoder-mel-weight 1.0 --init-checkpoint "$INIT" \
74
+ --mel-gan-weight 0.1 --gan-2d --gan-fm-auto --gan-r1-gamma 1.0 --gan-crop 128 --gan-warmup-steps 25000 \
75
+ --steps 60000 --batch-size 16 --lr 2e-4 --max-frames 1000 --en-upsample 2 --save-interval 5000 --device cuda
76
+
77
+ echo "### 7. Export + assess (sweep ckpts; ~35k is usually the held-out sweet spot) ###"
78
+ for K in 30000 35000 40000; do
79
+ $PY $S/export_8k.py --acoustic-ckpt $C/acoustic_8k/inflect-micro-fastspeech-$K.pt \
80
+ --vocoder-ckpt $C/vocoder_8k/hifigan-snake_8k-final.pt --out-dir $C/onnx_$K
81
+ $PY $S/synth_from_text.py --onnx-dir $C/onnx_$K --out-dir $C/eval_$K --texts eval_big.jsonl
82
+ $PY $S/synth_from_text.py --onnx-dir $C/onnx_$K --out-dir $C/entity_$K --texts eval_entity.jsonl
83
+ CUDA_VISIBLE_DEVICES='' $PY $S/assess_quality.py --synth-dir $C/eval_$K --tag ${TAG}_$K || true
84
+ done
85
+ echo "DONE -> pick best $C/onnx_<K>/ ; ship its *.onnx + meta.json to your model repo."