275 lines
15 KiB
Python
275 lines
15 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
生成《林夏》v2 全部新增语音资源
|
|
|
|
使用 edge-tts(微软 Azure 语音合成)
|
|
运行: python3 tools/generate_voices.py
|
|
|
|
角色 → 声优映射:
|
|
林夏 → zh-CN-XiaoxiaoNeural (温暖年轻女声)
|
|
小美 → zh-CN-XiaoyiNeural (活泼年轻女声)
|
|
阿哲 → zh-CN-YunxiNeural (年轻男声)
|
|
王姐 → zh-CN-XiaoxiaoNeural (用较慢语速模拟成熟感)
|
|
安安 → zh-CN-XiaoyiNeural (活泼,用较快语速)
|
|
室友A → zh-CN-XiaoyiNeural
|
|
室友B → zh-CN-XiaoxiaoNeural
|
|
妈妈 → zh-CN-XiaoxiaoNeural (慢速 + 柔和,模拟妈妈)
|
|
周南 → zh-CN-YunyangNeural (沉稳男声)
|
|
外卖小哥 → zh-CN-YunjianNeural (干脆男声)
|
|
"""
|
|
|
|
import asyncio
|
|
import os
|
|
import edge_tts
|
|
|
|
# ── 输出目录 ──
|
|
BASE = os.path.join(os.path.dirname(__file__), '..', 'audio', 'mp3')
|
|
DIR_LINXIA = os.path.join(BASE, 'linxia')
|
|
DIR_REACT = os.path.join(BASE, 'react')
|
|
|
|
# ── 声优配置 ──
|
|
V_LINXIA = 'zh-CN-XiaoxiaoNeural'
|
|
V_XIAOMEI = 'zh-CN-XiaoyiNeural'
|
|
V_AZHE = 'zh-CN-YunxiNeural'
|
|
V_WANGJIE = 'zh-CN-XiaoxiaoNeural' # 慢速
|
|
V_ANAN = 'zh-CN-XiaoyiNeural' # 快速
|
|
V_DORM_A = 'zh-CN-XiaoyiNeural'
|
|
V_DORM_B = 'zh-CN-XiaoxiaoNeural'
|
|
V_MOM = 'zh-CN-XiaoxiaoNeural' # 慢速 + 柔和
|
|
V_ZHOUNAN = 'zh-CN-YunyangNeural'
|
|
V_DELIVERY = 'zh-CN-YunjianNeural'
|
|
|
|
# ── 语速配置 (rate: "-20%" 较慢, "+10%" 较快) ──
|
|
# edge-tts rate 格式: "+0%", "-20%", "+15%" 等
|
|
|
|
# ══════════════════════════════════════════
|
|
# 林夏语音列表
|
|
# ══════════════════════════════════════════
|
|
|
|
LINXIA_LINES = [
|
|
# ── 通用短句 ──
|
|
('generic_hmm', '嗯。', '-5%'),
|
|
('generic_ok', '好。', '-5%'),
|
|
('generic_warm_ok', '嗯,好啊。', '-10%'),
|
|
('generic_cold_know', '知道了。', '+0%'),
|
|
('generic_cold_fine', '行吧。', '+0%'),
|
|
('generic_curious', '嗯?', '-5%'),
|
|
('generic_then', '然后呢?', '-5%'),
|
|
|
|
# ── MSG-01 妈妈电话 ──
|
|
('msg01_auto', '嗯……好。', '-15%'),
|
|
|
|
# ── MSG-02 小美 ──
|
|
('msg02_warm', '好啊,明天不见不散。', '-5%'),
|
|
('msg02_cold', '再说吧。', '+0%'),
|
|
('msg02_curious', '你怎么知道的啊?', '-5%'),
|
|
('msg02_r3_curious', '他……发了什么?', '-10%'),
|
|
|
|
# ── MSG-03 外卖 ──
|
|
('msg03_auto', '好,我下来拿。', '+0%'),
|
|
|
|
# ── MSG-04 阿哲 ──
|
|
('msg04_warm', '你那边……还好吗。', '-15%'),
|
|
('msg04_cold', '知道了。明天寄。', '+0%'),
|
|
('msg04_curious', '你怎么不自己来拿。', '-5%'),
|
|
('msg04_r3w_warm', '不太好。', '-20%'),
|
|
('msg04_r3w_cold', '挺好的。', '+0%'),
|
|
('msg04_r3c_curious', '为什么。', '-10%'),
|
|
('msg04_r3c_warm', '算了,不问了。', '-10%'),
|
|
|
|
# ── MSG-05 王姐 ──
|
|
('msg05_warm', '谢谢王姐。', '-5%'),
|
|
('msg05_cold', '好的,知道了。', '+0%'),
|
|
('msg05_curious', '王姐……有什么建议吗?', '-10%'),
|
|
|
|
# ── MSG-06 安安 ──
|
|
('msg06_warm', '在哪儿啊?我看看吧。', '-5%'),
|
|
('msg06_cold', '不了,你们玩好。', '+0%'),
|
|
('msg06_curious', '你喝了多少了?', '-5%'),
|
|
|
|
# ── MSG-07 寝室群 ──
|
|
('msg07_warm', '恭喜恭喜!太厉害了!', '-5%'),
|
|
('msg07_cold', '恭喜。', '+0%'),
|
|
('msg07_curious', '怎么升的啊?讲讲!', '-5%'),
|
|
('msg07_r3_warm', '挺好的,你请客我去!', '-5%'),
|
|
('msg07_r3_cold', '还行。', '+0%'),
|
|
|
|
# ── MSG-09 小美(醉)──
|
|
('msg09_warm', '小美……你没事吧。', '-15%'),
|
|
('msg09_cold', '我知道了。', '+0%'),
|
|
('msg09_curious', '你……为什么现在才说。', '-15%'),
|
|
('msg09_r3_warm', '我不恨你。', '-20%'),
|
|
('msg09_r3_cold', '我得想想。', '-10%'),
|
|
|
|
# ── MSG-10 妈妈文字 ──
|
|
('msg10_warm', '没有,怎么了?', '-5%'),
|
|
('msg10_cold', '嗯,要睡了。', '-10%'),
|
|
('msg10_curious', '妈……你能打电话给我吗?', '-15%'),
|
|
('msg10_call_warm', '没什么……就是有点累。', '-20%'),
|
|
('msg10_call_curious','妈,我辞职了。', '-15%'),
|
|
('msg10_rw_warm', '还好。', '-10%'),
|
|
('msg10_rw_curious', '不太好。', '-15%'),
|
|
|
|
# ── MSG-11 阿哲电话 ──
|
|
('msg11_warm', '可以。但只有五分钟。', '-5%'),
|
|
('msg11_cold', '不方便。我明天寄给你。', '+0%'),
|
|
('msg11_curious', '为什么非要今天来?', '-5%'),
|
|
('msg11_tap', '不了。', '+0%'),
|
|
|
|
# ── MSG-12 周南 ──
|
|
('msg12_warm', '周南!你还记得我啊!', '-5%'),
|
|
('msg12_cold', '嗯,你好。', '+0%'),
|
|
('msg12_curious', '你怎么有我微信的?', '-5%'),
|
|
('msg12_tap', '嗯……好久不见。', '-10%'),
|
|
|
|
# ── MSG-13 周南长语音 ──
|
|
('msg13_warm', '一个人在北京,还好吗?', '-10%'),
|
|
('msg13_cold', '你讲了好多。', '-5%'),
|
|
('msg13_curious', '生日快乐。', '-15%'),
|
|
('msg13_tap', '嗯,我都听到了。', '-10%'),
|
|
('msg13_r3_warm', '不太好。今天也是我的坏日子。', '-15%'),
|
|
('msg13_r3_cold', '还行吧。', '-5%'),
|
|
('msg13_r3_curious', '你为什么突然联系我?', '-10%'),
|
|
|
|
# ── MSG-14 妈妈电话 ──
|
|
('msg14_auto', '嗯,我没事。', '-10%'),
|
|
]
|
|
|
|
# ══════════════════════════════════════════
|
|
# NPC 反应语音列表
|
|
# ══════════════════════════════════════════
|
|
|
|
NPC_LINES = [
|
|
# ── MSG-02 小美反应 ──
|
|
('msg02_react_warm', V_XIAOMEI, '行!那我叫上安安!老地方见!别放鸽子啊!', '+5%'),
|
|
('msg02_react_cold', V_XIAOMEI, '……好吧。你要是改主意了随时说。', '-5%'),
|
|
('msg02_react_curious', V_XIAOMEI, '还不是阿哲自己发朋友圈!你不知道吗?他发了又秒删……', '+0%'),
|
|
('msg02_react_silent', V_XIAOMEI, '你还活着吗哈哈。好了不烦你了,有事找我。', '+5%'),
|
|
('msg02_react_tap', V_XIAOMEI, '嗯是什么意思啊哈哈!行吧你休息。', '+5%'),
|
|
('msg02_r3_curious', V_XIAOMEI, '就发了一张你们以前的照片,配了一句值得。两分钟就删了。', '-5%'),
|
|
|
|
# ── MSG-04 阿哲反应 ──
|
|
('msg04_react_warm', V_AZHE, '我……挺好的。你呢?', '-10%'),
|
|
('msg04_react_cold', V_AZHE, '嗯……谢了。', '-5%'),
|
|
('msg04_react_curious', V_AZHE, '我……不太方便过去。', '-10%'),
|
|
('msg04_react_silent', V_AZHE, '那……我晚点再说吧。', '-10%'),
|
|
('msg04_react_tap', V_AZHE, '谢谢。', '-5%'),
|
|
('msg04_r3w_sorry', V_AZHE, '……对不起。', '-20%'),
|
|
('msg04_r3w_good', V_AZHE, '那就好。', '-5%'),
|
|
('msg04_r3w_night', V_AZHE, '……那,晚安。', '-15%'),
|
|
('msg04_r3c_nevermind', V_AZHE, '我……没什么。你寄吧。', '-10%'),
|
|
|
|
# ── MSG-05 王姐反应 ──
|
|
('msg05_react_warm', V_WANGJIE, '别客气。你还年轻,出去看看也好。这行业你知道的。', '-15%'),
|
|
('msg05_react_curious', V_WANGJIE, '你想听真话?别在这行耗了。你有本事,去试试别的。', '-10%'),
|
|
|
|
# ── MSG-06 安安反应 ──
|
|
('msg06_react_warm', V_ANAN, '对面那个!我给你留了位置!赶紧赶紧!', '+10%'),
|
|
('msg06_react_cold', V_ANAN, '好吧!那我给你寄一杯!', '+10%'),
|
|
('msg06_react_curious', V_ANAN, '才两杯!我清醒得很!……好吧三杯。快来!', '+5%'),
|
|
('msg06_react_silent', V_ANAN, '喂?信号不好吗?好吧以后再约!', '+5%'),
|
|
('msg06_react_tap', V_ANAN, '嗯是来还是不来啊!算了回头聊!', '+5%'),
|
|
|
|
# ── MSG-07 寝室群反应 ──
|
|
('msg07_react_warm', V_DORM_A, '哈哈谢谢!我请客!你最近怎么样啊?', '+5%'),
|
|
('msg07_react_curious', V_DORM_A, '说来话长!我们老板走了我就顶上了哈哈。', '+5%'),
|
|
('msg07_react_tap', V_DORM_B, '林夏发表情了哈哈。', '+0%'),
|
|
('msg07_r3_cold', V_DORM_B, '你是不是有事啊?没事可以跟我们说。', '-5%'),
|
|
|
|
# ── MSG-09 小美(醉)反应 ──
|
|
('msg09_react_warm', V_XIAOMEI, '我没事……我就是喝多了想告诉你……你别怪我。', '-15%'),
|
|
('msg09_react_tap', V_XIAOMEI, '你不生气吗?算了……当我没说。', '-10%'),
|
|
('msg09_react_curious', V_XIAOMEI, '因为你今天……我怕你以后恨我……', '-15%'),
|
|
('msg09_r3_warm', V_XIAOMEI, '谢谢你……谢谢你林夏。', '-20%'),
|
|
('msg09_r3_cold', V_XIAOMEI, '好……对不起。', '-15%'),
|
|
|
|
# ── MSG-10 妈妈反应 ──
|
|
('msg10_react_warm', V_MOM, '没事,就是想起来问一下。今天怎么样?', '-20%'),
|
|
('msg10_react_curious', V_MOM, '怎么了夏夏?你声音不对。', '-15%'),
|
|
('msg10_call_warm', V_MOM, '累了就回家歇几天。妈煮了排骨。', '-20%'),
|
|
('msg10_call_curious', V_MOM, '辞了就辞了。回来再说。', '-15%'),
|
|
('msg10_call_silent', V_MOM, '不想说就不说。妈在这呢。', '-20%'),
|
|
('msg10_rw_warm', V_MOM, '那就好。别太晚了。', '-15%'),
|
|
('msg10_rw_curious', V_MOM, '怎么了?', '-10%'),
|
|
|
|
# ── MSG-11 阿哲反应 ──
|
|
('msg11_react_warm', V_AZHE, '好,我马上过来。谢谢。', '-5%'),
|
|
('msg11_react_cold', V_AZHE, '好吧……那谢了。', '-10%'),
|
|
('msg11_react_curious', V_AZHE, '我……明天要出差。就五分钟。', '-10%'),
|
|
('msg11_react_tap', V_AZHE, '好。', '-5%'),
|
|
|
|
# ── MSG-12 周南反应 ──
|
|
('msg12_react_warm', V_ZHOUNAN, '当然记得。你是我们班最安静的那个。', '-10%'),
|
|
('msg12_react_cold', V_ZHOUNAN, '嗯……好久不见。你现在在哪呢?', '-10%'),
|
|
('msg12_react_curious', V_ZHOUNAN, '你忘了吗?毕业那天你加的我。不过你可能早忘了哈哈。', '-5%'),
|
|
('msg12_react_silent', V_ZHOUNAN, '你是不是不记得我了?没关系的。就是突然想说声嗨。', '-10%'),
|
|
('msg12_react_tap', V_ZHOUNAN, '是啊,好久不见。', '-10%'),
|
|
|
|
# ── MSG-13 周南反应 ──
|
|
('msg13_react_warm', V_ZHOUNAN, '还行吧。习惯了。你呢?你在北京过得好吗?', '-10%'),
|
|
('msg13_react_cold', V_ZHOUNAN, '是吗?不好意思,话太多了。', '-10%'),
|
|
('msg13_react_curious', V_ZHOUNAN, '谢谢你。你是今天第一个跟我说的。', '-15%'),
|
|
('msg13_react_silent', V_ZHOUNAN, '不好意思突然说这么多……那,晚安。', '-10%'),
|
|
('msg13_react_tap', V_ZHOUNAN, '嗯。谢谢你听我说。', '-10%'),
|
|
('msg13_r3_warm', V_ZHOUNAN, '那我们算是难兄难妹了。', '-5%'),
|
|
('msg13_r3_cold', V_ZHOUNAN, '那就好。', '-5%'),
|
|
('msg13_r3_curious', V_ZHOUNAN, '因为……今天生日嘛,想起以前的事。想起你。', '-15%'),
|
|
|
|
# ── MSG-14 妈妈反应 ──
|
|
('msg14_react_linked', V_MOM, '好。那早点睡。妈爱你。', '-20%'),
|
|
('msg14_react_unlinked', V_MOM, '你今天怎么都不理妈妈?出什么事了吗?', '-15%'),
|
|
]
|
|
|
|
|
|
async def generate_one(voice, text, rate, outpath):
|
|
"""用 edge-tts 生成单条语音"""
|
|
comm = edge_tts.Communicate(text, voice, rate=rate)
|
|
await comm.save(outpath)
|
|
|
|
|
|
async def main():
|
|
os.makedirs(DIR_LINXIA, exist_ok=True)
|
|
os.makedirs(DIR_REACT, exist_ok=True)
|
|
|
|
tasks = []
|
|
total = len(LINXIA_LINES) + len(NPC_LINES)
|
|
done = 0
|
|
|
|
# ── 生成林夏语音 ──
|
|
for name, text, rate in LINXIA_LINES:
|
|
outpath = os.path.join(DIR_LINXIA, f'{name}.mp3')
|
|
if os.path.exists(outpath):
|
|
done += 1
|
|
continue
|
|
tasks.append((V_LINXIA, text, rate, outpath, name))
|
|
|
|
# ── 生成 NPC 反应语音 ──
|
|
for name, voice, text, rate in NPC_LINES:
|
|
outpath = os.path.join(DIR_REACT, f'{name}.mp3')
|
|
if os.path.exists(outpath):
|
|
done += 1
|
|
continue
|
|
tasks.append((voice, text, rate, outpath, name))
|
|
|
|
if not tasks:
|
|
print(f'所有 {total} 条语音已存在,无需生成。')
|
|
return
|
|
|
|
print(f'需要生成 {len(tasks)} 条语音(已跳过 {done} 条已存在的)...\n')
|
|
|
|
# 逐条生成(避免并发过高被限流)
|
|
for i, (voice, text, rate, outpath, name) in enumerate(tasks):
|
|
try:
|
|
await generate_one(voice, text, rate, outpath)
|
|
print(f' [{i+1}/{len(tasks)}] {name}: "{text}"')
|
|
except Exception as e:
|
|
print(f' [{i+1}/{len(tasks)}] FAILED {name}: {e}')
|
|
|
|
print(f'\n完成!共生成 {len(tasks)} 条语音。')
|
|
print(f' 林夏语音目录: {DIR_LINXIA}')
|
|
print(f' NPC反应目录: {DIR_REACT}')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
asyncio.run(main())
|