● Zion Boggan
repos/Stepping App/promo/voiceover/gen_vo.py
zionboggan.com ↗
72 lines · python
History for this file →
1
import json, numpy as np, soundfile as sf
2
from kokoro import KPipeline
3
 
4
SR = 24000
5
VOICE = "af_heart"
6
SPEED = 0.95
7
pipe = KPipeline(lang_code="a")
8
 
9
segs = [
10
    ("s1", "This is Stepping.", 0.55),
11
    ("s1", "The home for Divine Nine step show culture.", 0.70),
12
    ("s1", "Step shows, strolls, and the people behind them, all in one place.", 0.90),
13
 
14
    ("s2", "Start by choosing your city.", 0.50),
15
    ("s2", "Stepping is launching in Atlanta and Washington, D.C.", 0.85),
16
 
17
    ("s3", "Inside each city is The Line-Up.", 0.50),
18
    ("s3", "Every step show, stroll, and competition coming up.", 0.65),
19
    ("s3", "Tap any one to see the date, the time, and the details.", 0.85),
20
 
21
    ("s4", "On a show's page, you'll find everything you need to know.", 0.55),
22
    ("s4", "And you can join the conversation in the comments.", 0.85),
23
 
24
    ("s5", "Then there's The Yard, the community.", 0.50),
25
    ("s5", "Chapters, step teams, DJs, promoters, and classes.", 0.65),
26
    ("s5", "Everyone who makes the culture move.", 0.85),
27
 
28
    ("s6", "Each one has its own page,", 0.35),
29
    ("s6", "so you always know who's who.", 0.85),
30
 
31
    ("s7", "Traveling in for a show?", 0.45),
32
    ("s7", "The Route helps you plan the trip.", 0.50),
33
    ("s7", "Hotels, restaurants, places to go, and the nearest hospital, for every city.", 0.90),
34
 
35
    ("s8", "Stepping works right in your phone's browser.", 0.55),
36
    ("s8", "No app store. No accounts to figure out.", 0.55),
37
    ("s8", "Just open it, and start.", 0.90),
38
 
39
    ("s9", "And there's more on the way.", 0.50),
40
    ("s9", "Live streaming of shows, more cities across the country, and video from every set.", 0.95),
41
 
42
    ("s10", "Stepping.", 0.45),
43
    ("s10", "Built for the culture.", 0.0),
44
]
45
 
46
def synth(text):
47
    parts = [a for _, _, a in pipe(text, voice=VOICE, speed=SPEED)]
48
    return np.concatenate(parts).astype("float32")
49
 
50
out = []
51
t = 0.0
52
scene_start = {}
53
seg_log = []
54
for scene, text, gap in segs:
55
    if scene not in scene_start:
56
        scene_start[scene] = round(t, 3)
57
    w = synth(text)
58
    out.append(w)
59
    seg_log.append({"scene": scene, "start": round(t, 3), "dur": round(len(w) / SR, 3), "text": text})
60
    t += len(w) / SR
61
    if gap > 0:
62
        out.append(np.zeros(int(gap * SR), dtype="float32"))
63
        t += gap
64
 
65
full = np.concatenate(out)
66
sf.write("stepping_vo.wav", full, SR)
67
meta = {"voice": VOICE, "sr": SR, "speed": SPEED, "total": round(len(full) / SR, 3),
68
        "scene_start": scene_start, "segments": seg_log}
69
json.dump(meta, open("scene_times.json", "w"), indent=2)
70
print("TOTAL", meta["total"])
71
for s, st in scene_start.items():
72
    print(f"  {s}: {st}")