|
import abc |
|
import io |
|
import scipy.io.wavfile as wavfile |
|
from pydub import AudioSegment |
|
|
|
import traceback |
|
|
|
import gradio as gr |
|
|
|
|
|
|
|
def mix_background_music(original_audio, 背景音乐, TTS_up, bg_up): |
|
original_audio = original_audio + TTS_up |
|
|
|
ori_out = io.BytesIO() |
|
original_audio.export(ori_out, format="mp3") |
|
ori_out.seek(0) |
|
original_audio_bytes = ori_out.read() |
|
|
|
if 背景音乐 is None: |
|
return original_audio_bytes, None |
|
|
|
else: |
|
|
|
wav_io = io.BytesIO() |
|
wavfile.write(wav_io, 背景音乐[0], 背景音乐[1]) |
|
wav_io.seek(0) |
|
background_music = AudioSegment.from_wav(wav_io) |
|
|
|
background_music = background_music + bg_up |
|
|
|
|
|
combined = original_audio.overlay(background_music) |
|
|
|
|
|
mix_out = io.BytesIO() |
|
combined.export(mix_out, format="mp3") |
|
mix_out.seek(0) |
|
combined_audio_bytes = mix_out.read() |
|
|
|
return original_audio_bytes, combined_audio_bytes |
|
|
|
class Base_TTS(metaclass=abc.ABCMeta): |
|
default_show = False |
|
|
|
@abc.abstractmethod |
|
def _get_config_page(self): |
|
"""要求返回2个参数:config和inputs |
|
config: gr.Group对象,用于包裹所有的输入项 |
|
inputs: 一个列表,包含所有的输入项,用于后续的事件绑定,列表的顺序必须和后续_generate方法中参数args的顺序一致 |
|
|
|
例如: |
|
:return: config, inputs |
|
""" |
|
|
|
@abc.abstractmethod |
|
def _generate(self, text, *args): |
|
"""要求返回一个AudioSegment对象 |
|
|
|
:param text: str,要转换为语音的文本 |
|
:param args: 其他参数,与_get_config_page方法中的inputs一一对应 |
|
|
|
:return: original_audio |
|
|
|
示例: |
|
original_audio = AudioSegment.from_file(io.BytesIO(original_audio_bytes), format="mp3") |
|
return original_audio |
|
""" |
|
|
|
@abc.abstractmethod |
|
def get_name(self): |
|
"""要求返回一个str,是TTS的名称 |
|
|
|
:return: name |
|
|
|
示例: |
|
return '原神语音合成引擎' |
|
""" |
|
|
|
def is_show(self): |
|
""" |
|
是否显示这个TTS |
|
|
|
:return: True or False |
|
""" |
|
return True |
|
|
|
def _get_submit_button(self): |
|
""" |
|
要求返回1个参数:btn |
|
btn: gr.Button对象,是提交按钮 |
|
|
|
:return: btn |
|
""" |
|
btn = gr.Button(value="合成", variant="primary", interactive=True, visible=False) |
|
return btn |
|
|
|
def get_config_page(self): |
|
self.config, self.inputs = self._get_config_page() |
|
if self.default_show: |
|
self.config.visible = True |
|
else: |
|
self.config.visible = False |
|
|
|
def get_submit_button(self): |
|
self.btn = self._get_submit_button() |
|
if self.default_show: |
|
self.btn.visible = True |
|
else: |
|
self.btn.visible = False |
|
|
|
def set_visible(self, visible: bool): |
|
|
|
if not hasattr(self, 'config') or not hasattr(self, 'btn'): |
|
raise Exception('请先调用get_config_page和get_submit_button方法') |
|
|
|
self.btn.visible = visible |
|
self.config.visible = visible |
|
|
|
def create_interface_event(self, text, audio, TTS_up, bg_up, text_output, ori_audio_output, mix_audio_output): |
|
"""如果交互方法只是的点击提交按钮,可以不用重写这个方法""" |
|
|
|
|
|
if not hasattr(self, 'config') or not hasattr(self, 'btn'): |
|
raise Exception('请先调用get_config_page和get_submit_button方法') |
|
|
|
self.btn.click(self.generate, inputs=[ |
|
text, |
|
audio, |
|
TTS_up, |
|
bg_up |
|
] + self.inputs, |
|
outputs=[text_output, ori_audio_output, mix_audio_output]) |
|
|
|
def generate(self, text, 背景音乐, TTS_up, bg_up, *args): |
|
try: |
|
original_audio = self._generate(text, *args) |
|
|
|
return None, *mix_background_music(original_audio, 背景音乐, TTS_up, bg_up) |
|
except Exception as e: |
|
msg = traceback.format_exc() |
|
|
|
return msg + '\n\n' + str(e), None, None |
|
|
|
|
|
|