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) # 使用BytesIO导出合并后的音频为字节串 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): # 检查是否已经获取了config和btn 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): """如果交互方法只是的点击提交按钮,可以不用重写这个方法""" # 检查是否已经获取了config和btn 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