153 lines
6.6 KiB
Python
153 lines
6.6 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
播客对话生成脚本 (使用VoxCPM)
|
||
生成Sonia和Author的对话,不使用Judy
|
||
"""
|
||
|
||
import os
|
||
import json
|
||
from datetime import datetime
|
||
|
||
# 尝试导入VoxCPM
|
||
try:
|
||
from systems.voxcpm.voxcpm import VoxCPM
|
||
VOXCPM_AVAILABLE = True
|
||
except ImportError:
|
||
VOXCPM_AVAILABLE = False
|
||
print("警告: VoxCPM不可用,将使用模拟生成")
|
||
|
||
class PodcastGeneratorWithVoxCPM:
|
||
def __init__(self):
|
||
# 加载角色配置
|
||
config_path = "output/podcast/characters/character_config.json"
|
||
if os.path.exists(config_path):
|
||
with open(config_path, 'r', encoding='utf-8') as f:
|
||
self.config = json.load(f)
|
||
else:
|
||
# 如果配置文件不存在,使用默认配置
|
||
self.config = {
|
||
"Sonia": {"voice_model": "en-GB-RyanNeural"},
|
||
"Author": {"voice_model": "en-US-GuyNeural"}
|
||
}
|
||
|
||
# 尝试初始化VoxCPM
|
||
self.model = None
|
||
if VOXCPM_AVAILABLE:
|
||
try:
|
||
from systems.voxcpm.voxcpm import VoxCPM
|
||
LOCAL_MODEL_PATH = "/root/tts/VoxCPM/models/openbmb__VoxCPM1.5"
|
||
self.model = VoxCPM(
|
||
voxcpm_model_path=LOCAL_MODEL_PATH,
|
||
enable_denoiser=False, # 质量关键(匹配Ben的成功克隆配置)
|
||
optimize=False # 避免优化问题
|
||
)
|
||
print("✓ VoxCPM模型加载成功")
|
||
except Exception as e:
|
||
print(f"⚠️ VoxCPM初始化失败: {e}")
|
||
self.model = None
|
||
|
||
def create_podcast_script(self):
|
||
"""创建播客对话脚本"""
|
||
script = [
|
||
{
|
||
"speaker": "Sonia",
|
||
"text": "欢迎来到本期节目,今天我们有幸邀请到作者,一起回顾2001-2009年这段特殊的历史时期。这段时间被称为'韩信的入场券',充满了复杂的地缘政治变化。能否请您为我们概述一下这个时代的主要特点?"
|
||
},
|
||
{
|
||
"speaker": "Author",
|
||
"text": "这个时代最突出的特点是中国的战略隐忍。面对1999年大使馆被炸的屈辱、2001年南海撞机的紧张局势,中国选择了与美国合作反恐,从而获得了宝贵的发展窗口期。"
|
||
},
|
||
{
|
||
"speaker": "Sonia",
|
||
"text": "在2008年金融危机中,您特别提到了一个叫'高斯联结函数'的数学模型,以及它如何影响了亚洲歌神张学友的投资。这个数学模型究竟是如何运作的?"
|
||
},
|
||
{
|
||
"speaker": "Author",
|
||
"text": "这个模型由华裔数学家李祥林提出,它巧妙地'删除'了违约的相关性,使得一篮子高风险贷款可以被评级为AAA级资产。张学友投资的雷曼兄弟迷你债券正是被这种模型包装后的产品,导致他损失了约4000万港币。"
|
||
},
|
||
{
|
||
"speaker": "Sonia",
|
||
"text": "您提到了'瓦良格'号航母和普京寻求加入北约被拒的事件。这两件事看似无关,但它们如何共同构成了中国崛起的战略机遇?"
|
||
},
|
||
{
|
||
"speaker": "Author",
|
||
"text": "这是一个非常有趣的巧合。美国忙于反恐战争,无力阻止中国购买并改造'瓦良格'号;同时,北约拒绝普京的加入请求,迫使俄罗斯转向与中国合作。这两大因素为中国创造了有利的外部环境。"
|
||
},
|
||
{
|
||
"speaker": "Sonia",
|
||
"text": "最后一个问题,您认为2001-2009年这段时间为中国后来的发展奠定了怎样的基础?"
|
||
},
|
||
{
|
||
"speaker": "Author",
|
||
"text": "这十年是中国嵌入全球产业链、积累资本和技术的关键时期。通过隐忍和务实的战略,中国不仅成功避免了与美国的直接冲突,还利用了美国的战略重心转移,实现了经济的快速发展。"
|
||
},
|
||
{
|
||
"speaker": "Sonia",
|
||
"text": "感谢您今天的精彩分享,让我们更好地理解了这一段复杂而重要的历史。"
|
||
}
|
||
]
|
||
return script
|
||
|
||
def generate_audio_with_voxcpm(self, text, output_file):
|
||
"""使用VoxCPM生成音频"""
|
||
if self.model is None:
|
||
print(f"⚠️ VoxCPM不可用,创建模拟音频文件: {output_file}")
|
||
# 创建一个空的音频文件作为占位符
|
||
with open(output_file, 'w') as f:
|
||
f.write(f"Simulated audio for: {text}")
|
||
return
|
||
|
||
try:
|
||
# 使用VoxCPM生成音频
|
||
audio = self.model.generate(
|
||
text=text,
|
||
cfg_value=2.0,
|
||
inference_timesteps=20
|
||
)
|
||
|
||
# 保存音频文件
|
||
import soundfile as sf
|
||
sf.write(output_file, audio, self.model.tts_model.sample_rate)
|
||
print(f"✓ 生成音频: {output_file}")
|
||
|
||
except Exception as e:
|
||
print(f"✗ 生成音频失败 {output_file}: {e}")
|
||
# 创建一个错误文件作为占位符
|
||
with open(output_file.replace('.mp3', '_error.txt'), 'w') as f:
|
||
f.write(f"Error generating audio: {e}\nText: {text}")
|
||
|
||
def generate_podcast(self):
|
||
"""生成播客音频"""
|
||
script = self.create_podcast_script()
|
||
|
||
# 创建输出目录
|
||
output_dir = "output/podcast/interview"
|
||
os.makedirs(output_dir, exist_ok=True)
|
||
|
||
print(f"开始生成播客,共 {len(script)} 个片段...")
|
||
|
||
for i, line in enumerate(script):
|
||
speaker = line["speaker"]
|
||
text = line["text"]
|
||
|
||
# 生成音频文件
|
||
output_file = f"{output_dir}/{speaker.lower()}_{i+1:02d}.wav" # 使用wav格式以兼容soundfile
|
||
self.generate_audio_with_voxcpm(text, output_file)
|
||
|
||
# 创建脚本文件
|
||
script_file = f"{output_dir}/podcast_script.txt"
|
||
with open(script_file, 'w', encoding='utf-8') as f:
|
||
for line in script:
|
||
f.write(f"{line['speaker']}: {line['text']}\n\n")
|
||
|
||
print(f"\n✓ 播客脚本已保存到: {script_file}")
|
||
print(f"✓ 共处理 {len(script)} 个音频片段")
|
||
print("✓ 播客生成完成!")
|
||
|
||
def main():
|
||
generator = PodcastGeneratorWithVoxCPM()
|
||
generator.generate_podcast()
|
||
|
||
if __name__ == "__main__":
|
||
main() |