From 024ab4628ae86239bc0247fd662569b8214b7845 Mon Sep 17 00:00:00 2001 From: supotato6 <1322749946@qq.com> Date: Tue, 8 Jul 2025 07:20:55 +0000 Subject: [PATCH 01/21] fastspeech models --- .../common/batch_sampler/__init__.py | 1 + .../batch_sampler/text_batch_sampler.py | 61 ++ paddlex/inference/common/result/__init__.py | 1 + .../common/result/base_audio_result.py | 36 + paddlex/inference/common/result/mixin.py | 61 ++ paddlex/inference/models/__init__.py | 4 +- .../common/tokenizer/tokenizer_utils.py | 2 - .../common/tokenizer/tokenizer_utils_base.py | 12 +- .../models/text_to_pinyin/__init__.py | 15 + .../models/text_to_pinyin/predictor.py | 84 ++ .../models/text_to_pinyin/processors.py | 779 ++++++++++++++++++ .../inference/models/text_to_pinyin/result.py | 21 + .../text_to_speech_acoustic/__init__.py | 15 + .../text_to_speech_acoustic/predictor.py | 69 ++ .../text_to_speech_acoustic/processors.py | 14 + .../models/text_to_speech_acoustic/result.py | 21 + .../models/text_to_speech_vocoder/__init__.py | 15 + .../text_to_speech_vocoder/predictor.py | 70 ++ .../text_to_speech_vocoder/processors.py | 14 + .../models/text_to_speech_vocoder/result.py | 23 + paddlex/inference/utils/io/__init__.py | 1 + paddlex/inference/utils/io/writers.py | 35 +- paddlex/inference/utils/official_models.py | 3 + paddlex/modules/__init__.py | 8 + paddlex/modules/text_to_pinyin/__init__.py | 18 + .../modules/text_to_pinyin/dataset_checker.py | 27 + paddlex/modules/text_to_pinyin/evaluator.py | 27 + paddlex/modules/text_to_pinyin/exportor.py | 27 + paddlex/modules/text_to_pinyin/model_list.py | 18 + paddlex/modules/text_to_pinyin/trainer.py | 40 + .../text_to_speech_acoustic/__init__.py | 18 + .../dataset_checker.py | 27 + .../text_to_speech_acoustic/model_list.py | 17 + .../text_to_speech_vocoder/__init__.py | 18 + .../text_to_speech_vocoder/dataset_checker.py | 27 + .../text_to_speech_vocoder/model_list.py | 17 + test.wav | Bin 0 -> 73844 bytes 37 files changed, 1641 insertions(+), 5 deletions(-) create mode 100644 paddlex/inference/common/batch_sampler/text_batch_sampler.py create mode 100644 paddlex/inference/common/result/base_audio_result.py create mode 100644 paddlex/inference/models/text_to_pinyin/__init__.py create mode 100644 paddlex/inference/models/text_to_pinyin/predictor.py create mode 100644 paddlex/inference/models/text_to_pinyin/processors.py create mode 100644 paddlex/inference/models/text_to_pinyin/result.py create mode 100644 paddlex/inference/models/text_to_speech_acoustic/__init__.py create mode 100644 paddlex/inference/models/text_to_speech_acoustic/predictor.py create mode 100644 paddlex/inference/models/text_to_speech_acoustic/processors.py create mode 100644 paddlex/inference/models/text_to_speech_acoustic/result.py create mode 100644 paddlex/inference/models/text_to_speech_vocoder/__init__.py create mode 100644 paddlex/inference/models/text_to_speech_vocoder/predictor.py create mode 100644 paddlex/inference/models/text_to_speech_vocoder/processors.py create mode 100644 paddlex/inference/models/text_to_speech_vocoder/result.py create mode 100644 paddlex/modules/text_to_pinyin/__init__.py create mode 100644 paddlex/modules/text_to_pinyin/dataset_checker.py create mode 100644 paddlex/modules/text_to_pinyin/evaluator.py create mode 100644 paddlex/modules/text_to_pinyin/exportor.py create mode 100644 paddlex/modules/text_to_pinyin/model_list.py create mode 100644 paddlex/modules/text_to_pinyin/trainer.py create mode 100644 paddlex/modules/text_to_speech_acoustic/__init__.py create mode 100644 paddlex/modules/text_to_speech_acoustic/dataset_checker.py create mode 100644 paddlex/modules/text_to_speech_acoustic/model_list.py create mode 100644 paddlex/modules/text_to_speech_vocoder/__init__.py create mode 100644 paddlex/modules/text_to_speech_vocoder/dataset_checker.py create mode 100644 paddlex/modules/text_to_speech_vocoder/model_list.py create mode 100644 test.wav diff --git a/paddlex/inference/common/batch_sampler/__init__.py b/paddlex/inference/common/batch_sampler/__init__.py index 4a1ff0b680..957017c92f 100644 --- a/paddlex/inference/common/batch_sampler/__init__.py +++ b/paddlex/inference/common/batch_sampler/__init__.py @@ -20,3 +20,4 @@ from .markdown_batch_sampler import MarkDownBatchSampler from .ts_batch_sampler import TSBatchSampler from .video_batch_sampler import VideoBatchSampler +from .text_batch_sampler import TextBatchSampler \ No newline at end of file diff --git a/paddlex/inference/common/batch_sampler/text_batch_sampler.py b/paddlex/inference/common/batch_sampler/text_batch_sampler.py new file mode 100644 index 0000000000..c0118dc1fe --- /dev/null +++ b/paddlex/inference/common/batch_sampler/text_batch_sampler.py @@ -0,0 +1,61 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ....utils import logging +from .base_batch_sampler import BaseBatchSampler + + +class TextBatchSampler(BaseBatchSampler): + def __init__(self): + """Initializes the BaseBatchSampler. + + Args: + batch_size (int, optional): The size of each batch. Only support 1. + """ + super().__init__() + self.batch_size = 1 + + def sample(self, inputs): + """Generate list of input file path. + + Args: + inputs (str): file path. + + Yields: + list: list of file path. + """ + if isinstance(inputs, str): + yield [inputs] + else: + logging.warning( + f"Not supported input data type! Only `str` are supported, but got: {input}." + ) + + @BaseBatchSampler.batch_size.setter + def batch_size(self, batch_size): + """Sets the batch size. + + Args: + batch_size (int): The batch size to set. + + Raises: + Warning: If the batch size is not equal 1. + """ + # only support batch size 1 + if batch_size != 1: + logging.warning( + f"audio batch sampler only support batch size 1, but got {batch_size}." + ) + else: + self._batch_size = batch_size \ No newline at end of file diff --git a/paddlex/inference/common/result/__init__.py b/paddlex/inference/common/result/__init__.py index b6f42bf0fb..a7be7e8ed8 100644 --- a/paddlex/inference/common/result/__init__.py +++ b/paddlex/inference/common/result/__init__.py @@ -16,6 +16,7 @@ from .base_result import BaseResult from .base_ts_result import BaseTSResult from .base_video_result import BaseVideoResult +from .base_audio_result import BaseAudioResult from .mixin import ( Base64Mixin, CSVMixin, diff --git a/paddlex/inference/common/result/base_audio_result.py b/paddlex/inference/common/result/base_audio_result.py new file mode 100644 index 0000000000..2c07f86304 --- /dev/null +++ b/paddlex/inference/common/result/base_audio_result.py @@ -0,0 +1,36 @@ +# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .base_result import BaseResult +from .mixin import AudioMixin + + +class BaseAudioResult(BaseResult, AudioMixin): + """Base class for computer vision results.""" + + INPUT_AUDIO_KEY = "input_audio" + + def __init__(self, data: dict) -> None: + """ + Initialize the BaseAudioResult. + + Args: + data (dict): The initial data. + + Raises: + AssertionError: If the required key (`BaseAudioResult.INPUT_AUDIO_KEY`) are not found in the data. + """ + + super().__init__(data) + AudioMixin.__init__(self,'wav') diff --git a/paddlex/inference/common/result/mixin.py b/paddlex/inference/common/result/mixin.py index 70f8164365..64595a76d5 100644 --- a/paddlex/inference/common/result/mixin.py +++ b/paddlex/inference/common/result/mixin.py @@ -34,6 +34,7 @@ TextWriter, VideoWriter, XlsxWriter, + AudioWriter, ) @@ -592,7 +593,67 @@ def _is_video_file(file_path): f"The result has multiple video files need to be saved. But the `save_path` has been specified as `{save_path}`!" ) video_writer.write(save_path, video[list(video.keys())[0]], *args, **kwargs) + +class AudioMixin: + """Mixin class for adding Audio handling capabilities.""" + def __init__(self, backend, *args: List, **kwargs: Dict) -> None: + """Initializes AudioMixin. + + Args: + *args: Additional positional arguments to pass to the AudioWriter. + **kwargs: Additional keyword arguments to pass to the AudioWriter. + """ + self._backend = backend + self._save_funcs.append(self.save_to_audio) + + @abstractmethod + def _to_audio(self) -> Dict[str, np.array]: + """Abstract method to convert the result to a audio. + + Returns: + Dict[str, np.array]: The audio representation result. + """ + raise NotImplementedError + + @property + def audio(self) -> Dict[str, np.array]: + """Property to get the audio representation of the result. + + Returns: + Dict[str, np.array]: The audio representation of the result. + """ + return self._to_audio() + + def save_to_audio(self, save_path: str, *args: List, **kwargs: Dict) -> None: + """Saves the audio representation of the result to the specified path. + + Args: + save_path (str): The path to save the audio. If the save path does not end with .mp4 or .avi, it appends the input path's stem and suffix to the save path. + *args: Additional positional arguments that will be passed to the audio writer. + **kwargs: Additional keyword arguments that will be passed to the audio writer. + """ + + def _is_audio_file(file_path): + mime_type, _ = mimetypes.guess_type(file_path) + return mime_type is not None and mime_type.startswith("audio/") + + audio_writer = AudioWriter(backend=self._backend, *args, **kwargs) + audio = self._to_audio() + if not _is_audio_file(save_path): + fn = Path(self._get_input_fn()) + stem = fn.stem + suffix = fn.suffix if _is_audio_file(fn) else ".mp4" + base_save_path = Path(save_path) + for key in audio: + save_path = base_save_path / f"{stem}_{key}{suffix}" + audio_writer.write(save_path.as_posix(), audio[key], *args, **kwargs) + else: + if len(audio) > 1: + logging.warning( + f"The result has multiple audio files need to be saved. But the `save_path` has been specified as `{save_path}`!" + ) + audio_writer.write(save_path, audio[list(audio.keys())[0]], *args, **kwargs) class MarkdownMixin: """Mixin class for adding Markdown handling capabilities.""" diff --git a/paddlex/inference/models/__init__.py b/paddlex/inference/models/__init__.py index cc26d1de0d..54ceb33ebb 100644 --- a/paddlex/inference/models/__init__.py +++ b/paddlex/inference/models/__init__.py @@ -50,7 +50,9 @@ from .ts_forecasting import TSFcPredictor from .video_classification import VideoClasPredictor from .video_detection import VideoDetPredictor - +from .text_to_speech_acoustic import Fastspeech2Predictor +from .text_to_speech_vocoder import PwganPredictor +from .text_to_pinyin import TextToPinyinPredictor def create_predictor( model_name: str, diff --git a/paddlex/inference/models/common/tokenizer/tokenizer_utils.py b/paddlex/inference/models/common/tokenizer/tokenizer_utils.py index b73b6b01c6..27b3fd746e 100644 --- a/paddlex/inference/models/common/tokenizer/tokenizer_utils.py +++ b/paddlex/inference/models/common/tokenizer/tokenizer_utils.py @@ -927,8 +927,6 @@ def init_chat_template(self, chat_template: Union[str, dict]): raise ValueError("Receive error chat_template data: ", chat_template) def save_resources(self, save_directory): - super().save_resources(save_directory) - if isinstance( self.chat_template, ChatTemplate ): # Future remove if ChatTemplate is deprecated diff --git a/paddlex/inference/models/common/tokenizer/tokenizer_utils_base.py b/paddlex/inference/models/common/tokenizer/tokenizer_utils_base.py index 126d44dcff..7bfb0add93 100644 --- a/paddlex/inference/models/common/tokenizer/tokenizer_utils_base.py +++ b/paddlex/inference/models/common/tokenizer/tokenizer_utils_base.py @@ -36,6 +36,8 @@ import numpy as np from .....utils import logging +from .....utils.download import download +from .....utils.cache import CACHE_DIR __all__ = [ "AddedToken", @@ -1661,7 +1663,15 @@ def from_pretrained(cls, pretrained_model_name_or_path, *args, **kwargs): resolved_vocab_files = {} for file_id, file_path in vocab_files.items(): # adapt to PaddleX - resolved_vocab_files[file_id] = file_path + if file_path is None or os.path.isfile(file_path): + resolved_vocab_files[file_id] = file_path + continue + else: + download_path = os.path.join( + CACHE_DIR, "official_models", pretrained_model_name_or_path, file_id + ) + download(file_path, download_path) + resolved_vocab_files[file_id] = download_path for file_id, file_path in resolved_vocab_files.items(): if resolved_vocab_files[file_id] is not None: diff --git a/paddlex/inference/models/text_to_pinyin/__init__.py b/paddlex/inference/models/text_to_pinyin/__init__.py new file mode 100644 index 0000000000..7214ddba8f --- /dev/null +++ b/paddlex/inference/models/text_to_pinyin/__init__.py @@ -0,0 +1,15 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .predictor import TextToPinyinPredictor diff --git a/paddlex/inference/models/text_to_pinyin/predictor.py b/paddlex/inference/models/text_to_pinyin/predictor.py new file mode 100644 index 0000000000..a03f6bb0fd --- /dev/null +++ b/paddlex/inference/models/text_to_pinyin/predictor.py @@ -0,0 +1,84 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np + +from ....utils.func_register import FuncRegister +from ...common.batch_sampler import TextBatchSampler + +from ..base import BasePredictor +from .result import TextToPinyinResult +from ....modules.text_to_pinyin.model_list import MODELS + + +class TextToPinyinPredictor(BasePredictor): + + entities = MODELS + + def __init__(self, *args, **kwargs): + """Initializes TextSegmentPredictor. + + Args: + *args: Arbitrary positional arguments passed to the superclass. + **kwargs: Arbitrary keyword arguments passed to the superclass. + """ + super().__init__(*args, **kwargs) + self.model = self._build() + + def _build_batch_sampler(self): + """Builds and returns an TextBatchSampler instance. + + Returns: + TextBatchSampler: An instance of TextBatchSampler. + """ + return TextBatchSampler() + + def _get_result_class(self): + """Returns the result class, TextToPinyinResult. + + Returns: + type: The TextToPinyinResult class. + """ + return TextToPinyinResult + + def _build(self): + """Build the model. + + Returns: + G2PWOnnxConverter: An instance of G2PWOnnxConverter. + """ + from .processors import ( + G2PWOnnxConverter, + ) + + # build model + model = G2PWOnnxConverter( + model_dir=self.model_dir, style="pinyin", enable_non_tradional_chinese=True + ) + return model + + def process(self, batch_data): + """ + Process a batch of data through the preprocessing, inference, and postprocessing. + + Args: + batch_data (List[Union[str], ...]): A batch of input text data. + + Returns: + dict: A dictionary containing the input path and result. The result include the output pinyin dict. + """ + result = self.model(batch_data[0]) + return { + "result": [result] + } diff --git a/paddlex/inference/models/text_to_pinyin/processors.py b/paddlex/inference/models/text_to_pinyin/processors.py new file mode 100644 index 0000000000..9b0e2cb00b --- /dev/null +++ b/paddlex/inference/models/text_to_pinyin/processors.py @@ -0,0 +1,779 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Credits + This code is modified from https://github.com/GitYCC/g2pW +""" +import json +import os +import re +from typing import Any +from typing import Dict +from typing import List +from typing import Tuple + +import jieba.posseg as psg +import numpy as np +import onnxruntime +from opencc import OpenCC +from ..common.tokenizer import BertTokenizer +from ....utils.download import download +from pypinyin import pinyin +from pypinyin import Style +from collections import OrderedDict + +INITIALS = [ + 'b', 'p', 'm', 'f', 'd', 't', 'n', 'l', 'g', 'k', 'h', 'zh', 'ch', 'sh', + 'r', 'z', 'c', 's', 'j', 'q', 'x' +] + +FINALS = [ + 'a', 'ai', 'ao', 'an', 'ang', 'e', 'er', 'ei', 'en', 'eng', 'o', 'ou', + 'ong', 'ii', 'iii', 'i', 'ia', 'iao', 'ian', 'iang', 'ie', 'io', 'iou', + 'iong', 'in', 'ing', 'u', 'ua', 'uai', 'uan', 'uang', 'uei', 'uo', 'uen', + 'ueng', 'v', 've', 'van', 'vn' +] + +SPECIALS = ['sil', 'sp'] +simplified_charcters = "制咖片型超声盘鉴定仔点他命书歌粉巾字帐恤手指记忆棒形转弯沟光○〇㐄㐅㐆㐌㐖毒㐜㐡㐤㐰㐺㑇㑳㒳㒸㔾㗂㗎㝵㞎㞙㞞以㢲㢴㤅㥁㥯㨗㫺㬎㮎㮚㮸㲋㲱㲾㳮涧㵪㶸㷖㷭㹢㹴犬㺢狓㺵碗㽮㿝䍃䔢䖟䖸䗈䗥䗪䝓射䥯䦉䯝鲃鱼䲔䳗鹅䵹鼄䶑一对应映射丁不识下儿子做二休世丘之貉并中台原则串为甚谓干净了百事无成八变五十些人得道鸡升天代如并来去个国政策劲幽灵在欧洲游荡接样萝卜坑侧化传价元论醇共再准刀两断切分耕耘收获钱货物向看旧就绪险刻千金动劳永逸匙零夜半卡通回复返影踪反常态口咬气句话同吐快吹周味呼诺呜品红锅哄而散起唱和问三知生熟团漆黑火糟堆场空块面塌糊涂尘染壁厢夔已足多情露水大早到晚夫妻当关万莫开失古恨套所料既往孔见提师要家主审寸阴难买斗牛小撮部阵局展身层巴掌帆风顺席地带过年计于春头载四季期被蛇怕井绳度愿式份弹顷深前律径心意念差愁孤行俱全房厅交遮打技长把抓死拿眼泪鼻涕钥锁折段抿拍即合扫排掬挥拨拥上入击洞掷揽改故辙败文值名斑方面旁族日秋餐隔雅里终父旦时晌会霎间晃暴寒曝更月望垠际朝夕本正经利杯羹东西板枝独秀根筋杆进条龙服务概模次函数又性程总付步脚印趋登毛拔呵氧氮碳决雌雄波未平派谎言流清楚白准溜烟潭有获闻是处降琴鹤甲病发可拾沙目然了直以相眨穿睹瞥瞬矢的解石鸟神教秉虔诚秘种窝蜂穷窍笑置笔苟勾销抹杀煞等奖箍节吃箭仇双雕诗筹箩筐系列纸级士官统丝毫挂维网尽线微吭响股脑胎脉承腔臂力致效资源址器举功投般说讲规贸易叶障着慎满皆输号木电池衣倾钟高低视仁觉醒览遗角银币触溃九鼎蔽抄出驷马追重语破贫洗贯走路安蹴至几蹶振跃役胆汗较辈轮辞赞退六连遍递边针血锤音错门思闪真倒项栽雾类保护川先惊乍体哄鳞爪鸣滴泡邻域党专鼓作齐炒丑烯亥克内酯冬加奴卯肝炎基尺梁街裤镐客宠庭巳汝昌烷玲磊糖肇酉醛啷青县韪良香骨鲷丂七集河市弦喜嘴张舌堵区工业姊妹星架构巧彩扭歪拼凑余热曜武州爷浮屠美乡老阶树荤素碎落能魄鳃鳗珠丄丅丆万俟丈尚摸母娘量管群亚虎必我堂令申件装伏位博侠义界表女墟台戏臭皮匠胜诸葛亮赛顶倍催请运算包立叉戟离疫苗土史志演围揭瓦晒夷姑婆帝村宝烂尖杉碱屉桌山岔岛由纪峡坝库镇废从德后拗汤治旬食明昧曹朋友框栏极权幂曲归依猫民氟硼氯磷铁江侗自旅法司洋浦梅园温暖湾焦班幸用田略番叠皇炮捶硝苯酸腺苷棱草镜穗跳远索锦纲聚氰胺联店胚膲爱色堇紫罗兰芝茶饭菱云虫藏藩乱叛苏亲债凳学座恐恋柱测肌腹衩锥系貂企乌跪叩军车农题迭都甘油屯奏键短阿姨陪姐只顾茅庐槽驾魂鲜鹿页其菜单乘任供势午齿汉组织吊调泻唇坡城报坟外夸将尉建筑岸岗公床扬新剑升杭林栗校楼标款汽社浣海商馆剧院钢华港机械广媒环球融第医科证券综财乐育游涨犹岭疏瘾睑确兵领导缴肢膛船艾瑟尔苍蔡虞效衫覆访诉课谕议轨述野钩限敌鞋颌颔颚饶首龈站例修凡划垂届属崽颏厨拜挫摆放旋削棋榻槛礼沉注滑营狱画确仪聘花葬诏员跌辖周达酒锚闸陷陆雨雪飞威丌于丹久乏予理评产亢卑亦乎舞己悲矩圆词害志但住佞佳便俗信票案幅翁倦伦假偏倚斜亏鬼敲停备伤脾胃仅此像俭匮免宜穴焉戴兼容许冻伯仲负彼昼皂轩轾实刊划颠卫战哥比省非好黄饰别拘束掩奶睬选择摇扰烦苦枚写协厌及格受欢迎约只估侵犯割状告或缺抗拒挽撤救药喻磨灭端倪少逆逾越避靠适吉誉吝玉含延咎歹听啻渊善谋均匀堪忍够太惹妙妥妨孕症孝术室完纳推冠积宣疑辩栗碴称屈挠屑干涉衡待很忙恶忿怎么怠急耻恭息悦惑惜惟想愉愧怍慌愤启懂懈怀材才紧招认扣抵拉舍也罢插揣冒搭撞南墙扩核支攻敢雷攀敬里吗需景智暇曾罪遇朽枉止况竞争辱求愈渝溶济左右袒困补爽特寂寞示弱找谢畏强疾徐痛痒冤符眠睦瞅董何厚云措活疲羞者轻玻璃祥兆禁移稂莠稳佛换答简结果盟绝缕途给谈否羁翼耐肖胫毋宁兴舒若菲莱痕迹窠臼虚衰脸兔撒鹰棺范该详讳抬泰让须眉象众赀账费灰赖奇虑训辍辨菽麦辛近送透逞徒速续逮捕遂遑违逊斧钺艰醉锈随观弃显饱脂肪使丏丐帮丒且慢末丕替桃宗王尊凉爵各图屋脊粮署录坛吾禄职胄袭君厦丗北壑桐疹损逢陵鹬丙寅戌氨腈唑纶辰酮脱氢酶醚丞丢现掉纱帽弄扯炮碗丠両丣坐存激肩臻蒂莲悖序驱丨丩丫挺杈髻鬟细介俄伊犁京尼布订普渡央委监察检查剂圈设警队斯督剩震境航舶革防托播促质版蝾螈锋研艺历残消频谱精密制造陲邮候埔坚压坜凹汇执府究邦俘摄寮彬狼岳肺肿庸英讯诊埋粒胞括控码韩暑枪枢砥澳哇牟寿甸钻探篇签缀缝继耳肯照妇埃悬璧轴柜台辣搁浅邪跑纤阮阳私囊魔丮丰姿采丱烧丳丵丶丷丸参寨朗桂瑞砂衷霞貌凤仆舰因嫌宰峰干络牌持旨祭祷簿编罚宾办丼丿乀乂乃乄仰慕盛旷留考验阔乆乇么丑麽乊湖燃乑乒乓乕乖僻忤戾离谬迕乗危肥劫除隙浪婿乙炔肠酰吡咯盐乚乛乜嘢卿玄宫尾狐龟塔嶷兄弟泉章霄钉耙乞扎哀怜恕讨乢乣乤乥乧乨乩童乪乫乭乳晕汁液瑶浆牙癌突窦罩腐胶猪酪蛋糕菌瘤乴乵乶乷乸乹乺乼乾俸冰嘉哕嚎坤妈尸垒旱枯涸俐渴潮涩煸豆燥爹瘦瘪癣瞪袋脆姜贝隆馏乿亀亁叫咕攘扔搞男砸窜蓬麻亃亄亅却亇迟典今临繁累卵奉婚聪躬巨与迁添裂副宿岁怪恶尕仑愣杆硅硫钛铀锰芑杂异钠砷胂磺琥珀舱棍簧胡茬盗浩盆贩郎腿亍洪亐互欠助勉惠操斥诿系户译亓墓碑刑铃卅渠缤纷斗米旗宪钒灯徽瘟祖拳福谷丰脏腑绑肉腌苓蕴桥铺霸颜闹判喷冈底蛙陉矿亖亘亜罕们娜桑那努哈喀弗烈曼松森杜氏杯奥琛敦戊穆圣裔汇薛孙亟亡佚虏羊牢奋释卷卸契媾感额睫缠谊趾塞挤纽阻还配驰庄亨洛祚亪享津沪畿郊慈菴枇杷膏亭阁锃丽亳亶亹诛初责翻疯偶杰丛稠妖拖寰居吸授慧蜗吞壮魅狗矛盾益渣患忧稀描猿梦暂涯畜祸缘沸搜引擎臣横纭谁混援蒸兽狮税剖亻亼亽亡什献刹邡么仂仃仄仆富怨仈仉毕昔晨壳绍仍仏仒仕宦仗欺恃腰叹叹炬梓讫施仙后琼逝仚仝仞仟悔仡佬偿填泊拓扑簇羔购顿钦佩发棻阃驭养亿儆尤借帧赈凌叙帖李柔刚沃眦睚戒讹取飨读仨仫仮著泳卧躺韶夏裁仳仵唯贤凭钓诞仿似宋佛讽伀硕盼鹅伄儅伈伉俪柯始娃迈戈坦堡帕茨萨庙玛莉莎藤霍姆伋伍奢胥廷芳豪伎俩侍汛勒希羲雏伐憩整谟闲闲伕伙伴颐伜伝伢叔恒兹恩翰伱伲侣伶俜悧鼬伸懒缩喇叭伹伺伻伽倻辐伾似佃伫布乔妮墨佉卢佌贷劣廉昂档浓矮伞洼缓耗胸谷迷挡率龋宅沫舍疗佐贰佑占优据铧尝呢须鲁晓佗佘余坪寺瓜铳僧蒙芒陀龛哼呕坊奸孽弊揖祟茧缚誓贼佝偻瞀佟你夺赶佡佢佣佤佧贾佪佫佯佰佱洁绩酿肴佴卷佶佷佸佹佺佻佼佽佾具唤窘坏娱怒慨硬习惯聋膨胀蔓骇贵痹侀侁侂侃侄侅鸿燕侇侈糜靡侉侌妾侏儒仓鼠侐侑侔仑侘侚链侜偎傍钴循柳葫芦附価侮骂蔑侯岩截蚀局贴壶嬛宴捷携桶笺酌俣狭膝狄俅俉俊俏俎俑俓俔谚俚俛黎健呈固墒增守康箱湿祐镖镳杠盒靖膜龄俞豹猎噪孚封札筒托衍鸽剪撰稿炼厂禊练缮葺俯瞰撑冲效俳俴俵俶俷俺备俾伥倂倅储卒惶敷猝逃颉蓄崇隐倌倏忽刺蜡烛噍嚼坍扁抽毙葱楣灌灶粪背薮卖赔闭霉腾倓倔幸倘倜傥倝借箸挹浇阅倡狂倢倣値倥偬倨傲倩匡嗣冲柝珍倬倭寇猩倮倶倷倹勤赞偁偃充伪吏嗓寐惺扮拱芫茜藉虢钞偈伟晶偌宕距析滤殿疼瘫注颇偓偕鸭歇滞偝偟偢忘怡旺偨偩逼偫偭偯偰偱偲侦缉蹄偷减惰漏窥窃偸偺迹傀儡傅傈僳骂篱傎奎琳迪叟芭傒傔傕伧悉荒傜傞傢傣芽逼佣婢傮睨寄檄诵谣颂伛担辜弓惨蒿悼疤傺傻屄臆巢泄箧羡盖轧颓傿㑩僄僇佥僊働僎侨僔僖僚僝伪僣僤侥僦猴偾僩僬僭僮僯僰雇僵殖签静僾僿征陇儁侬儃儇侩朴薄儊儋儌儍傧儓俦侪拟尽儜儞儤儦儩汰哉寡渥裕酷儭儱罐儳儵儹傩俨儽兀臬臲鹫允勋勋宙宵帅憝彝谐嫂阋畅沛溢盈饥赫凶悍狠猛顽愚妣斩秦遣鞭耀敏荣槃泽爆碟磁秃缆辉霁卤朵娄孜烽酱勃汀箕裘钳耶蒙蕾彻兑软遭黜兎児韵媳爸兕觥兖兙兛兜售鍪肚兝兞兟兡兢兣樽殓涅睡禀籍赘泌啡肽奸幕涵涝熵疚眷稃衬讧赴焕椒歼植跏没试误猜栖窗肋袖颊兪卦撇胡岐廓轿疸枫茴珑厕秩募勺吨寓斤历亩迫筷厘最淫螺韬兮宽匪筛襄赢轭复兲诈刃堰戎痞蚁饷它冀铸冂冃円冇冉册嫁厉砺竭醮冏牧冑冓冔冕冖冗冘冞冢窄抑诬冥冫烘菇蛰冷凝坨橇淇淋炭饼砖碛窖醋雕雹霜冱冶炉艳嘲峻滩淡漠煖飕饮冼冽凃凄怆梗凅凇净凊凋敝蒙凔凛遵汞脢凞几凢処凰凯凵凶焰凸折刷纹预丧喽奔巡榜殡芙蓉租笼辑鞘萃凼锯镬刁蛮刂娩崩批拆摊掰蘖骤歧颗秒袂赃勿嘱忌磋琢肤刈羽刎讼戮舂桨艇刓刖霹雳刜创犊刡恙墅帜筵致劫劫刨昏默攸尿欲熏润薰圭删刮痧铲刱刲刳刴刵踏磅戳柏槐绣芹苋猬舟铭鹄鹜劫剁剃辫刭锉履铅克剌姻咽哨廊掠桅沿召瞻翅赵卜渺茫郭剒剔剕沥剚愎毅讷才剜剥啄采剞剟剡剣剤䌽剐肾驶黏剰袍剀紊铲剸剺剽剿劁劂札劈啪柴扳啦刘奭姥夼昫涓熙禅禹锡翔雁鹗刽刿弩柄蜻蛉劒劓劖劘劙澜篑赏矶釜晋甜薪逐劦熔纣虐赤囚劬劭労劵效劻劼劾峭艮勅勇励勍勐腊脖庞漫饲荡粥辄勖勗勘骄馁碌泮雇捐竹骑殊阱绩朴恳谨剿勧勩勯勰劢勋勷劝惩慰诫谏勹芡践阑匁庇拯粟扎袱裹饺匆遽匈匉匊匋匍匐茎匏匕妆痰脓蛹斋苑烤蹈塘羌熊阀螳螂疆碚竿纬荷茵邙魏匚匜匝匟扶稷匣匦拢匸匹耦匽匾匿卂叮疮禧轸堤棚迢钧炼卄卆遐卉瓷盲瓶当胱腱裸卋卌卍卐怯污贱鄙龌龊陋卓溪唐梯渔陈枣泥漳浔涧梨芬谯赡辕迦郑単驴弈洽鳌卛占筮卝卞卟吩啉屎翠厄卣卨卪卬卮榫袄玺绶钮蚤惧殆笃耸卲帘帙绕恤卼卽厂厎厓厔厖厗奚厘厍厜厝谅厕厤厥厪腻孢厮厰厳厣厹厺粕垢芜菁厼厾叁悟茸薯叄吵笄悌哺讥坫垄弧芯杠潜婴刍袁诘贪谍煽馈驳収岳缔灾贿骗叚叡吻拦蘑蜜诀燧玩砚筝椎蔺铜逗骊另觅叨唠谒杵姓喊嚷嚣咚咛塑寻恼憎擦只泣渗蝠叱吒咄咤喝籀黛舵舷叵叶铎懿昭穰苴辽叻叼吁堑嫖赌瞧爬众抒吅吆夥卺橡涤抱纵摩郡唁坠扇篮膀袜颈吋忾谘酬哭妓媛暗表缰迩妃羿絮蕃浑拐葵暮隅吔吖啶嗪戚吜啬噬咽吟哦咏吠吧唧嗒咐吪隽咀征燐苞茹钙哧吮吰吱嘎吲哚吴栋娇窟孟箫忠晗淞阖闾趼宇呐睛嘘拂捧疵熄竽笛糠吼吽呀吕韦蒙呃呆笨呇贡呉罄呋喃呎呏呔呠呡痴呣呤呦呧瑛眩扒晬淑姬瑜璇鹃呪呫哔嚅嗫呬呯呰呱呲咧噌钝呴呶呷呸呺呻哱咻啸噜吁坎坷逻呿咁咂咆哮咇咈咋蟹煦珅蔼咍咑咒诅咔哒嚓咾哝哩喱咗咠咡咢咣咥咦咨嗟询咩咪咫啮啮咭咮咱咲咳呛嗽咴啕咸咹咺呙喉咿婉恸悯赋矜绿茗蓝哂抢瞒哆嗦啰噻啾滨彗哋哌哎唷哟哏哐哞哢哤哪里哫啼喘哰哲萎蚌哳咩哽哿呗唅唆唈唉唎唏哗尧棣殇璜睿肃唔睇唕吣唞唣喳唪唬唰喏唲唳唵嘛唶唸唹唻唼唾唿啁啃鹦鹉啅埠栈榷祺铺鞅飙啊啍啎啐啓啕啖啗啜哑祈啢衔啤啥啫啱啲啵啺饥啽噶昆沁喁喂喆裙喈咙喋喌喎喑喒喓喔粗喙幛庆滋鹊喟喣喤喥喦喧骚喨喩梆吃葡萄喭驼挑吓碰枞瓣纯疱藻趟铬喵営喹喺喼喿嗀嗃嗄嗅嗈嗉嗊嗍嗐嗑嗔诟嗕嗖嗙嗛嗜痂癖嗝嗡嗤嗥嗨唢嗬嗯嗰嗲嗵叽嗷嗹嗾嗿嘀嘁嘂嘅惋嘈峪禾荫啀嘌嘏嘐嘒啯啧嘚唛嘞嘟囔嘣嘥嘦嘧嘬嘭这谑严敞馋松哓嘶嗥呒虾嘹嘻啴嘿噀噂噅噇噉噎噏噔噗噘噙噚咝噞噢噤蝉皿噩噫噭嗳噱哙噳嚏涌洒欲巫霏噷噼嚃嚄嚆抖哜尝嚔苏嚚嚜嚞嚟呖嚬嚭嚮嚯亸喾饬按竣苛嚵嘤啭冁呓膪谦囍囒囓囗囘萧酚飘溅谛囝溯眸纥銮鹘囟殉囡団囤囥囧囨囱囫囵囬囮囯囲図囶囷囸囹圄圉拟囻囿圀圂圃圊粹蠹赦圌垦圏滚鲱凿枘圕圛圜圞坯埂壤骸炕祠窑豚绅魠鲮鳖圧握圩圪垯圬圮圯炸岬幔毯祇窨菩溉圳圴圻圾坂坆沾坋坌舛壈昆垫墩椅坒坓坩埚坭坰坱坳坴坵坻坼杨挣涎帘垃垈垌垍垓垔垕垗垚垛垝垣垞垟垤垧垮垵垺垾垿埀畔埄埆埇埈埌殃隍埏埒埕埗埜垭埤埦埧埭埯埰埲埳埴埵埶绋埸培怖桩础辅埼埽堀诃侄庑堃堄摧磐贞韧砌堈堉垩堋堌堍堎垴堙堞堠礁堧堨舆堭堮蜓摘堲堳堽堿塁塄塈煤茔棵塍垲埘塓绸塕鸦沽虱塙冢塝缪塡坞埙塥塩塬塱场螨塼塽塾塿墀墁墈墉墐夯増毁墝墠墦渍钵墫墬堕墰墺墙橱壅壆壊壌壎壒榨蒜壔壕壖圹垆壜壝垅壡壬壭壱売壴壹壻壸寝壿夂夅夆変夊夌漱邑夓腕泄甥御骼夗夘夙衮瑙妊娠醣枭珊莺鹭戗幻魇夤蹀秘擂鸫姚宛闺屿庾挞拇賛蛤裨菠氅漓捞湄蚊霆鲨箐篆篷荆肆舅荔鲆巷惭骰辟邱镕镰阪漂烩鲵鲽鳄鸨胪鹏妒峨谭枰晏玑癸祝秤竺牡籁恢罡蝼蝎赐绒御梭夬夭砣榆怙枕夶夹馅奄崛葩谲奈贺祀赠奌奂奓奕䜣詝奘奜奠奡奣陶奨奁魁奫奬奰娲孩贬隶酥宄狡猾她姹嫣妁毡荼皋膻蝇嫔妄妍嫉媚娆妗趣妚妞妤碍妬娅妯娌妲妳妵妺姁姅姉姗姒姘姙姜姝姞姣姤姧姫姮娥姱姸姺姽婀娀诱慑胁娉婷娑娓娟娣娭娯娵娶娸娼婊婐婕婞婤婥溪孺婧婪婬婹婺婼婽媁媄媊媕媞媟媠媢媬媮妫媲媵媸媺媻媪眯媿嫄嫈袅嫏嫕妪嫘嫚嫜嫠嫡嫦嫩嫪毐嫫嫬嫰妩嫺娴嫽嫿妫嬃嬅嬉耍婵痴艳嬔嬖嬗嫱袅嫒嬢嬷嬦嬬嬭幼嬲嬴婶嬹嬾嬿孀娘孅娈孏曰癫屏孑孓雀孖斟篓谜摺孛矻鸠崮轲祜鸾孥邈毓棠膑孬孭孰孱孳孵泛罔衔孻孪宀宁冗拙株薇掣抚琪瓿榴谧弥宊濂祁瑕宍宏碁宓邸谳実潢町宥宧宨宬徵崎骏掖阙臊煮禽蚕宸豫寀寁寥寃檐庶寎暄碜寔寖寘寙寛寠苫寤肘洱滥蒗陕核寪弘绰螽宝擅疙瘩晷対檐専尃尅赎绌缭畴衅尌峙醌襟痲碧屁昊槌淘恵瀑牝畑莓缸羚觑蔻脏躁尔尓锐尗尙尜尟尢尥尨尪尬尭尰擒尲尶尴尸尹潽蠖蛾尻扣梢蚴鳍脬蹲屇屌蚵屐屃挪屖屘屙屛屝屡屣峦嶂岩舄屧屦屩屪屃屮戍驻钾崖嵛巅旮旯楂榄榉芋茱萸靛麓屴屹屺屼岀岊岌岍阜岑彭巩岒岝岢岚岣岧岨岫岱岵岷峁峇峋峒峓峞峠嵋峨峰峱岘峹峿崀崁崆祯崋崌崃岖昆崒崔嵬巍萤颢崚崞崟崠峥巆崤崦崧殂岽崱崳崴崶崿嵂嵇嵊泗嵌嵎嵒嵓岁嵙嵞嵡嵩嵫嵯嵴嵼嵾嵝崭崭晴嶋嶌嶒嶓嵚崂嶙嶝嶞峤嶡嶢峄嶨嶭嶮嶰嶲岙嵘巂巃巇巉岿巌巓巘巛滇芎巟巠弋回巣巤炊擘蜥蟒蛊觋巰蜀彦淖杏茂甫楞巻巽帼巿帛斐鲫蕊帑帔帗帚琉汶帟帡帣帨裙帯帰帷帹暆帏幄帮幋幌幏帻幙帮幞幠幡幢幦幨幩幪帱幭幯幰遥蹉跎馀庚鉴幵幷稚邃庀庁広庄庈庉笠庋跋庖牺庠庤庥鲸庬庱庳庴庵馨衢庹庿廃厩廆廋廌廎廏廐廑廒荫廖廛厮搏锣廞弛袤廥廧廨廪廱绵踵髓廸迫瓯邺廻廼廾廿躔弁皱弇弌弍弎弐弑吊诡憾荐弝弢弣弤弨弭弮弰弪霖繇焘斌旭溥骞弶弸弼弾彀彄别累纠强彔彖彘彟彟陌彤贻彧绘虹彪炳雕蔚鸥彰瘅彲彳彴仿彷徉徨彸彽踩敛旆徂徇徊渭畲铉裼従筌徘徙徜徕膳苏萌渐徬徭醺徯徳徴潘徻徼忀瘁胖燎怦悸颤扉犀澎湃砰恍惚绞隘忉惮挨饿忐忑忒忖応忝忞耿忡忪忭忮忱忸怩忻悠懑怏遏怔怗怚怛怞怼黍讶怫怭懦怱怲恍怵惕怸怹恁恂恇恉恌恏恒恓恔恘恚恛恝恞恟恠恣恧眄恪恫恬澹恰恿悀悁悃悄悆悊悐悒晦悚悛悜悝悤您悩悪悮悰悱凄恻德悴怅惘闷悻悾惄愫钟蒐惆惇惌惎惏惓惔惙惛耄惝疟浊恿惦德恽惴蠢惸拈愀愃愆愈愊愍愐愑愒愓愔愕恪氓蠢騃昵惬赧悫愬愮愯恺愼慁恿慅慆慇霭慉慊愠慝慥怄怂慬慱悭慴慵慷戚焚憀灼郁憃惫憋憍眺捏轼愦憔憖憙憧憬憨憪憭怃憯憷憸憹憺懃懅懆邀懊懋怿懔懐懞懠懤懥恹懫懮懰懱毖懵遁梁雍忏懽戁戄戆戉戋戕戛戝戛戠戡戢戣戤戥戦戬戭戯轰戱披菊牖戸戹戺戻卯戽锹扂楔扃扆扈扊杖牵绢铐镯赉扐搂搅烊盹瞌跟趸镲靶鼾払扗玫腮扛扞扠扡扢盔押扤扦扱罾揄绥鞍郤窾扻扼扽抃抆抈抉抌抏瞎抔缳缢擞抜拗択抨摔歉蹿牾抶抻搐泵菸拃拄拊髀抛拌脯拎拏拑擢秧沓曳挛迂拚拝拠拡拫拭拮踢拴拶拷攒拽掇芥橐簪摹疔挈瓢骥捺蹻挌挍挎挐拣挓挖掘浚挙揍聩挲挶挟挿捂捃捄捅捆捉捋胳膊揎捌捍捎躯蛛捗捘捙捜捥捩扪捭据捱捻捼捽掀掂抡臀膘掊掎掏掐笙掔掗掞棉芍掤搪阐掫掮掯揉掱掲掽掾揃揅揆搓揌诨揕揗揘揜揝揞揠揥揩揪揫橥遒麈揰揲揵揶揸背揺搆搉搊搋搌搎搔搕撼橹捣搘搠搡搢搣搤搥搦搧搨搬楦裢讪赸掏搰搲搳搴揾搷搽搾搿摀摁摂摃摎掴摒摓跤摙摛掼摞摠摦喉羯摭摮挚摰摲抠摴抟摷掺摽撂撃撅稻撊撋挦锏泼撕撙撚㧑挢撢掸撦撅撩撬撱朔揿蚍蜉挝捡擀掳闯擉缶觚擐擕擖擗擡擣擤澡腚擧擨擩擫擭摈拧撷擸撸擽擿攃摅撵攉攥攐攓撄搀撺每攩攫辔澄攮攰攲攴轶攷砭讦攽碘敁敃敇敉叙敎筏敔敕敖闰诲敜煌敧敪敳敹敺敻敿斁衽斄牒绉诌斉斎斓鹑谰驳鳢斒筲斛斝斞斠斡斢斨斫斮晾沂潟颖绛邵斲斸釳於琅斾斿旀旗旃旄涡旌旎旐旒旓旖旛旝旟旡旣浴旰獭魃旴时旻旼旽昀昃昄昇昉晰躲澈熹皎皓矾昑昕昜昝昞昡昤晖笋昦昨是昱昳昴昶昺昻晁蹇隧蔬髦晄晅晒晛晜晞晟晡晢晤晥曦晩萘莹顗晿暁暋暌暍暐暔暕煅旸暝暠暡曚暦暨暪朦胧昵暲殄冯暵暸暹暻暾曀晔昙曈曌曏曐暧曘曙曛叠昽曩骆曱甴肱曷牍禺锟曽沧耽朁朅朆杪栓夸竟粘绦朊膺朏朐朓朕朘朙瞄觐溘饔飧朠朢朣栅椆淀虱朩朮朰朱炆璋钰炽鹮朳槿朵朾朿杅杇杌陧欣钊湛漼楷瀍煜玟缨翱肇舜贽适逵杓杕杗杙荀蘅杝杞脩珓筊杰榔狍閦颦缅莞杲杳眇杴杶杸杻杼枋枌枒枓衾葄翘纾逋枙狸桠枟槁枲枳枴枵枷枸橼枹枻柁柂柃柅柈柊柎某柑橘柒柘柙柚柜柞栎柟柢柣柤柩柬柮柰柲橙柶柷柸柺査柿栃栄栒栔栘栝栟柏栩栫栭栱栲栳栴檀栵栻桀骜桁镁桄桉桋桎梏椹葚桓桔桕桜桟桫椤桭杯桯桲桴桷桹湘溟梃梊梍梐潼栀枧梜梠梡梣梧梩梱梲梳梴梵梹棁棃樱棐棑棕榈簑绷蓑枨棘棜棨棩棪棫棬棯棰棱棳棸棹椁棼碗椄苕椈椊椋椌椐椑椓椗検椤椪椰椳椴椵椷椸椽椿楀匾楅篪楋楍楎楗楘楙楛楝楟楠楢楥桢楩楪楫楬楮楯楰梅楸楹楻楽榀榃榊榎槺榕榖榘榛狉莽搒笞榠榡榤榥榦榧杩榭榰榱梿霰榼榾桤槊闩槎槑槔槖様槜槢槥椠槪槭椮槱槲槻槼槾樆樊樏樑樕樗樘樛樟樠樧樨権樲樴樵猢狲桦樻罍樾樿橁橄橆桡笥龠橕橚橛辆椭橤橧竖膈跨橾橿檩檃檇柽檍檎檑檖檗桧槚檠樯檨檫檬梼槟檴檵柠棹櫆櫌栉櫜椟櫡槠栌枥榇栊櫹棂茄櫽欀欂欃欐欑栾欙棂溴欨欬欱欵欶欷歔欸欹欻欼欿歁歃歆艎歈歊莳蝶歓歕歘歙歛歜欤歠蹦诠镶蹒跚升陟歩歮歯歰歳歴璞歺瞑歾殁夭殈殍殑殗殜殙殛殒殢殣殥殪殚僵殰殳荃殷殸殹蛟殻肴谤殴毈毉喂毎毑蕈毗毘毚茛邓毧毬毳毷毹毽毾毵牦氄氆靴氉氊氇氍氐聊氕氖気氘氙氚氛氜氝氡汹焊痉氤氲氥氦铝锌氪烃氩铵痤汪浒漉痘盂碾菖蒲蕹蛭螅氵冰氹氺氽烫氾氿渚汆汊汋汍汎汏汐汔汕褟汙汚汜蓠沼秽蔑汧汨汩汭汲汳汴堤汾沄沅沆瀣沇沈葆浸沦湎溺痼疴沌沍沏沐沔沕沘浜畹砾沚沢沬沭沮沰沱灢沴沷籽沺烹濡洄泂肛泅泆涌肓泐泑泒泓泔泖泙泚泜泝泠漩馍涛粼泞藓鳅泩泫泭泯铢泱泲洇洊泾琵琶荽蓟箔洌洎洏洑潄濯洙洚洟洢洣洧洨洩痢滔洫洮洳洴洵洸洹洺洼洿淌蜚浄浉浙赣渫浠浡浤浥淼瀚浬浭翩萍浯浰蜃淀苔蛞蝓蜇螵蛸煲鲤浃浼浽溦涂涊涐涑涒涔滂莅涘涙涪涫涬涮涴涶涷涿淄淅淆淊凄黯淓淙涟淜淝淟淠淢淤渌淦淩猥藿亵淬淮淯淰淳诣涞纺淸淹炖癯绮渇済渉渋渓渕涣渟渢滓渤澥渧渨渮渰渲渶渼湅湉湋湍湑湓湔黔湜湝浈湟湢湣湩湫湮麟湱湲湴涅満沩溍溎溏溛舐漭溠溤溧驯溮溱溲溳溵溷溻溼溽溾滁滃滉滊荥滏稽滕滘汇滝滫滮羼耷卤滹浐煎漈漊漎绎漕漖漘漙沤漜漪漾漥漦漯漰溆漶漷濞潀颍潎潏潕潗潚潝潞潠潦祉疡潲潵滗潸潺潾涠澁澂澃澉澌澍澐澒澔澙渑澣澦澧澨澫澬浍澰澴澶澼熏郁濆濇濈濉濊貊濔疣濜濠濩觞浚濮盥潍濲泺瀁滢渎渖瀌浏瀒瀔濒泸瀛潇潆瀡潴泷濑瀬弥潋瀳瀵瀹瀺瀼沣滠灉灋灒漓灖灏灞灠滦灥灨滟灪蜴灮烬獴灴灸灺炁炅鱿炗炘炙炤炫疽烙钎炯炰炱炲炴炷毁炻烀烋瘴鲳烓烔焙烜烝烳饪烺焃焄耆焌焐焓焗焜焞焠焢焮焯焱焼煁煃煆煇煊熠煍熬煐炜煕暖熏硷霾煚煝煟煠茕矸煨琐炀萁煳煺煻熀熅熇熉罴荧穹炝熘熛熜稔谙烁熤熨熯熰眶蚂颎熳熸熿燀烨燂燄盏燊燋燏燔隼燖焖燠燡灿燨燮燹燻燽燿爇爊爓爚爝爟爨蟾爯爰为爻丬爿牀牁牂牄牋窗牏牓窗釉牚腩蒡虻牠虽蛎牣牤牮牯牲牳牴牷牸牼绊牿靬犂犄犆犇犉犍犎犒荦犗犛犟犠犨犩犪犮犰狳犴犵犺狁甩狃狆狎狒獾狘狙黠狨狩狫狴狷狺狻豕狈蜘猁猇猈猊猋猓猖獗猗猘狰狞犸猞猟獕猭猱猲猳猷猸猹猺玃獀獃獉獍獏獐獒毙獙獚獜獝獞獠獢獣獧鼇蹊狯猃獬豸狝獯鬻獳犷猕猡玁菟玅玆玈珉糁禛郅玍玎玓瓅玔玕玖玗玘玞玠玡玢玤玥玦珏瑰玭玳瑁玶玷玹玼珂珇珈瑚珌馐馔珔珖珙珛珞珡珣珥珧珩珪佩珶珷珺珽琀琁陨玡琇琖琚琠琤琦琨琫琬琭琮琯琰琱琲琅琴珐珲瑀瑂瑄瑉玮瑑瑔瑗瑢瑭瑱瑲瑳瑽瑾瑿璀璨璁璅璆璈琏璊璐璘璚璝璟璠璡璥瑷璩璪璫璯璲玙璸璺璿瓀璎瓖瓘瓒瓛脐瓞瓠瓤瓧瓩瓮瓰瓱瓴瓸瓻瓼甀甁甃甄甇甋甍甎甏甑甒甓甔瓮甖甗饴蔗甙诧钜粱盎锈团甡褥産甪甬甭甮宁铠甹甽甾甿畀畁畇畈畊畋畎畓畚畛畟鄂畤畦畧荻畯畳畵畷畸畽畾疃叠疋疍疎箪疐疒疕疘疝疢疥疧疳疶疿痁痄痊痌痍痏痐痒痔痗瘢痚痠痡痣痦痩痭痯痱痳痵痻痿瘀痖瘃瘈瘉瘊瘌瘏瘐痪瘕瘖瘙瘚瘛疭瘜瘝瘗瘠瘥瘨瘭瘆瘯瘰疬瘳疠瘵瘸瘺瘘瘼癃痨痫癈癎癐癔癙癜癠疖症癞蟆癪瘿痈発踔绀蔫酵皙砬砒翎翳蔹钨镴皑鹎驹暨粤褶皀皁荚皃镈皈皌皋皒朱皕皖皘皜皝皞皤皦皨皪皫皭糙绽皴皲皻皽盅盋碗盍盚盝踞盦盩秋千盬盭眦睁瞤盯盱眙裰盵盻睐眂眅眈眊県眑眕眚眛眞眢眣眭眳眴眵眹瞓眽郛睃睅睆睊睍睎困睒睖睙睟睠睢睥睪睾睯睽睾眯瞈瞋瞍逛瞏瞕瞖眍䁖瞟瞠瞢瞫瞭瞳瞵瞷瞹瞽阇瞿眬矉矍铄矔矗矙瞩矞矟矠矣矧矬矫矰矱硪碇磙罅舫阡、矼矽礓砃砅砆砉砍砑砕砝砟砠砢砦砧砩砫砮砳艏砵砹砼硇硌硍硎硏硐硒硜硖砗磲茚钡硭硻硾碃碉碏碣碓碔碞碡碪碫碬砀碯碲砜碻礴磈磉磎硙磔磕磖磛磟磠磡磤磥蹭磪磬磴磵磹磻硗礀硚礅礌礐礚礜礞礤礧礮砻礲礵礽礿祂祄祅祆禳祊祍祏祓祔祕祗祘祛祧祫祲祻祼饵脔锢禂禇禋祦禔祎隋禖禘禚禜禝禠祃禢禤禥禨禫祢禴禸秆秈秊闱飒秋秏秕笈蘵赁秠秣秪秫秬秭秷秸稊稌稍稑稗稙稛稞稬秸稲稹稼颡稿穂穄穇穈穉穋稣贮穏穜穟秾穑穣穤穧穨穭穮穵穸窿阒窀窂窅窆窈窕窊窋窌窒窗窔窞窣窬黩蹙窑窳窴窵窭窸窗竁竃竈竑竜并竦竖篦篾笆鲛竾笉笊笎笏笐靥笓笤箓笪笫笭笮笰笱笲笳笵笸笻筀筅筇筈筎筑筘筠筤筥筦笕筒筭箸筰筱筳筴宴筸箂个箊箎箑箒箘箙箛箜篌箝箠箬镞箯箴箾篁筼筜篘篙篚篛篜篝篟篠篡篢篥篧篨篭篰篲筚篴篶篹篼箦簁簃簆簉簋簌簏簜簟簠簥簦簨簬簰簸簻籊藤籒籓籔签籚篯箨籣籥籧笾簖籫籯芾麴籵籸籹籼粁秕粋粑粔粝粛粞粢粧粨粲粳稗粻粽辟粿糅糆糈糌糍糒糔萼糗蛆蹋糢糨糬粽糯糱籴粜糸糺紃蹼鲣霉纡纨绔纫闽襻紑纰纮锭鸢鹞纴紞紟扎紩紬绂绁纻紽紾绐絁絃絅経絍绗絏缡褵絓絖絘絜绚絣螯絪絫聒絰絵绝絺絻絿綀绡綅绠绨绣綌綍綎捆綖綘継続缎绻綦綪线綮綯绾罟蝽綷縩绺绫緁绲緅緆缁绯緌緎総緑绱緖缃缄缂绵缗緤褓缌纂緪緰缑缈缏缇縁縃縄萦缙缒縏缣縕缞縚缜缟缛縠縡縢縦绦縯縰骋缧縳纤缦絷缥縻衙縿繄缫繈繊繋繐缯繖繘繙繠缋繣繨缰缲繸繻缱纁纆纇缬缵纩纑纕缵纙纚纛缾罃罆坛罋罂罎罏罖罘罛罝罠罣罥罦罨罫罭锾罳罶罹罻罽罿羂羃羇芈蕉51鸵羑羖羌羜羝羢羣羟羧羭羮羰羱羵羶羸藜鲐翀翃翅翊翌翏翕翛翟翡翣翥翦跹翪翫翚翮翯翱翽翾翿板饕鸹锨耋耇耎耏专耒耜耔耞耡耤耨耩耪耧耰鬓耵聍聃聆聎聝聡聦聱聴聂聼阈聿肄肏肐肕腋肙肜肟肧胛肫肬肭肰肴肵肸肼胊胍胏胑胔胗胙胝胠铨胤胦胩胬胭胯胰胲胴胹胻胼胾脇脘脝脞脡脣脤脥脧脰脲脳腆腊腌臜腍腒腓胨腜腠脶腥腧腬腯踝蹬镣腴腶蠕诽膂腽嗉膇膋膔腘膗膙膟黐膣膦膫膰膴膵膷脍臃臄臇臈臌臐臑臓膘臖臙臛臝臞臧蓐诩臽臾臿舀舁鳑鲏舋舎舔舗馆舝舠舡舢舨舭舲舳舴舸舺艁艄艅艉艋艑艕艖艗艘艚艜艟艣舣艨艩舻艬艭荏艴艳艸艹艻艿芃芄芊萰陂藭芏芔芘芚蕙芟芣芤茉芧芨芩芪芮芰鲢芴芷芸荛豢芼芿苄苒苘苙苜蓿苠苡苣荬苤苎苪镑苶苹苺苻苾茀茁范蠡萣茆茇茈茌茍茖茞茠茢茥茦菰茭茯茳藨茷藘茼荁荄荅荇荈菅蜢鸮荍荑荘豆荵荸荠莆莒莔莕莘莙莚莛莜莝莦莨菪莩莪莭莰莿菀菆菉菎菏菐菑菓菔芲菘菝菡菢菣菥蓂菧菫毂蓥菶菷菹醢菺菻菼菾萅萆苌萋萏萐萑萜萩萱萴莴扁萻葇葍葎葑荭葖葙葠葥苇葧葭药葳葴葶葸葹葽蒄蒎莼茏薹莅蒟蒻蒢蒦蒨蒭藁蒯蒱鉾蒴蒹蒺蒽荪蓁蓆蓇蓊蓌蓍蓏蓓蓖蓧蓪蓫荜跣藕苁蓰蓱莼蓷蓺蓼蔀蔂蔃蔆蔇蔉蔊蔋蔌蔎蔕蔘蔙蒌蔟锷蒋雯茑蔯蔳麻蔵蔸蔾荨蒇蕋蕍荞蕐蕑芸莸蕖蕗蕝蕞蕠蕡蒉蕣蕤蕨蕳蓣蕸蕺蕻薀薁薃薅薆荟薉芗薏薐蔷薖薘剃谔钗薜薠薢薤薧薨薫薬薳薶薷薸薽薾薿藄藇藋荩藐藙藚藟藦藳藴苈藷藾蘀蘁蕲苹蘗蘘蘝蘤蘧蘩蘸蘼虀虆虍蟠虒虓虖虡虣虥虩虬虰蛵蛇虷鳟虺虼蚆蚈蚋蚓蚔蚖蚘蚜蚡蚣蚧蚨蚩蚪蚯蚰蜒蚱蚳蚶蚹蚺蚻蚿蛀蛁蛄蛅蝮蛌蛍蛐蟮蛑蛓蛔蛘蛚蛜蛡蛣蜊蛩蛱蜕螫蜅蚬蜈蝣蜋蜍蜎蜑蠊蜛饯蜞蜣蜨蜩蜮蜱蜷蜺蜾蜿蝀蝃蝋蝌蝍蝎蝏蝗蝘蝙蝝鲼蝡蝤蝥猿蝰虻蝲蝴蝻螃蠏蛳螉螋螒螓螗螘螙螚蟥螟螣螥螬螭䗖螾螀蟀蟅蝈蟊蟋蟑蟓蟛蟜蟟蟢虮蟨蟪蟭蛲蟳蛏蟷蟺蟿蠁蠂蠃虿蠋蛴蠓蚝蠗蠙蠚蠛蠜蠧蟏蠩蜂蠮蠰蠲蠵蠸蠼蠽衁衄衄衇衈衉衋衎衒同衖胡衞裳钩衭衲衵衹衺衿袈裟袗袚袟袢袪袮袲袴袷袺袼褙袽裀裉袅裋夹裍裎裒裛裯裱裲裴裾褀褂褉褊裈褎褐褒褓褔褕袆褚褡褢褦褧褪褫袅褯褰褱裆褛褽褾襁褒襆裥襉襋襌襏襚襛襜裣襞襡襢褴襦襫襬襭襮襕襶襼襽襾覂覃覅霸覉覊覌覗觇覚覜觍觎覧覩觊觏覰観觌觔觕觖觜觽觝觡酲觩觫觭觱觳觯觷觼觾觿言赅讣訇訏訑訒诂讬訧訬訳訹证訾詀詅诋毁詈詊讵詑诒诐詗诎察詨诜詶詸詹詻诙诖誂誃诔锄诓誋诳诶悖誙诮诰誧説読誯谇訚谄谆諆諌诤诹诼諕谂谀諝谝諟喧谥諴諵谌谖誊謆謇歌謍謏謑谡谥謡謦謪谪讴謷謼谩哗譅譆譈譊讹譒撰谮鑫譞噪譩谵譬譱譲谴譸譹谫讅讆詟䜩雠讐谗谶讙谠讟谽豁豉豇岂豊豋豌豏豔豞豖豗豜豝豣豦豨豭豱豳豵豶豷豺豻貅貆狸猊貔貘䝙貜貤餍贳餸贶贲赂賏赊赇赒賝赓赕賨赍斗賮賵賸赚赙赜赟贉赆赑贕赝赬赭赱赳迄趁趂趄趐趑趒趔趡趦趫趮趯趱趴趵趷趹趺趿跁跂跅跆踬跄跐跕跖跗跙跛跦跧跩跫跬跮跱跲跴跺跼跽踅踆踈踉踊踒踖踘踜踟躇蹰踠踡踣踤踥踦踧跷踫踮逾踱踊踶踹踺踼踽躞蹁蹂躏蹎蹐蹓蹔跸蹚蹜蹝迹蹠蹡蹢跶蹧蹩蹪蹯鞠蹽躃躄躅踌跻躐踯跞躘躙躗躝躠蹑躜躧躩躭躰躬躶軃軆辊軏轫軘軜軝腭転軥軨軭軱轱辘軷轵轺軽軿輀輂辇辂辁輈挽輗辄辎辋輠輤輬輭輮辏輴輵輶輹輼辗辒轇轏轑轒辚轕轖轗轘轙轝轞轹轳罪辣辞辵辶辺込辿迅迋迍麿迓迣迤逦迥迨迮迸迺迻迿逄逅逌逍逑逓迳逖逡逭逯逴逶逹遄遅侦遘遛遝遢遨遫遯遰遴绕遹遻邂邅邉邋邎邕邗邘邛邠邢邧邨邯郸邰邲邳邴邶邷邽邾邿郃郄郇郈郔郕郗郙郚郜郝郞郏郠郢郪郫郯郰郲郳郴郷郹郾郿鄀鄄郓鄇鄈鄋鄍鄎鄏鄐鄑邹邬鄕郧鄗鄘鄚鄜鄞鄠鄢鄣鄤鄦鄩鄫鄬鄮鄯鄱郐鄷鄹邝鄻鄾鄿酃酅酆酇郦酊酋酎酏酐酣酔酕醄酖酗酞酡酢酤酩酴酹酺醁醅醆醊醍醐醑醓醖醝酝醡醤醨醪醭醯醰酦醲醴醵醸醹醼醽醾釂酾酽釆釈鲈镏阊钆钇钌钯钋鼢鼹钐钏釪釬釭釱钍釸钕钫鈃钭鈆鈇钚鈊鈌钤钣鈒鈤钬钪鈬铌铈钶铛钹铍钸钿鉄鉆铊铇鉌铋鉏铂钷铆钵鉥钲鉨钼钽鉱鉲鉶铰铒鉼铪銍銎铣銕镂铫铦铑铷銤铱铟銧铥铕铯銭銰焊銶锑锉汞鋂锒鋆鋈鋊铤鋍铗鋐鋑鋕鋘鋙锊锓锔锇铓鋭铖锆锂铽鋳鋹鋺鉴镚钎錀锞锖锫锩錍铔锕錔锱铮锛錞锬锜錤錩錬録铼錼锝钔锴鍉镀鍏鍐铡鍚锻锽锸锲锘鍫鍭鍱鍴锶鍹锗针锺锿镅鎉鎋鎌鎍鎏鎒鎓鎗镉鎚鎞镃鎤铩锼鎭鎯镒镍鎴镓鎸鎹镎镟鏊镆镠镝鏖铿锵鏚镗镘镛鏠鏦錾镤鏸镪鏻鏽鏾铙鐄鐇鐏铹镦镡鐗馗镫镢镨鐡锎镄鐩镌鐬鐱镭鐶鐻鐽镱鑀鑅镔鑐鑕鑚鑛鑢鑤镥鑪镧鑯鑱鑴鑵镊镢钃镻闫闬闶闳閒闵閗閟阂関合閤哄阆閲阉閺阎阏阍阌暗闉阕阗闑闒闿闘闚阚闟闠闤闼阞阢阤阨阬阯阹阼阽陁陑陔陛陜陡陥陬骘陴険陼陾阴隃隈隒隗隞隠隣隤隩隮隰颧隳隷隹雂雈雉雊雎雑雒雗雘雚雝雟雩雰雱驿霂霅霈霊沾霒霓霙霝霢霣霤霨霩霪霫霮靁叇叆靑靓靣腼靪靮靰靳靷靸靺靼靿鞀鞃鞄鞍鞗鞙鞚鞝鞞鞡鞣鞨鞫鞬鞮鞶鞹鞾鞑韅鞯驮韍韎韔韖韘韝韫韡韣韭韭韱韹韺頀刮頄顸顼頍颀颃颁頖頞頠頫頬颅頯頲颕頼悴顋顑颙颛颜顕顚顜颟顣颥颞飐飑台飓颸飏飖颽颾颿飀飂飚飌翻飡飣饲飥饨饫飮飧飶餀餂饸饹餇餈饽哺馂餖餗餚馄馃餟餠餤餧餩餪餫糊餮糇餲饧馎糕饩馈馊馌馒饇馑馓膳饎饐饘饟馕馘馥馝馡馣骝骡馵馹駃駄駅駆駉駋驽駓驵駗骀驸駜骂骈駪駬骃駴骎駹駽駾騂騄骓騆騉騋骒骐麟騑騒験騕骛騠騢騣騤騧骧騵驺骟騺蓦骖骠骢驆驈骅驌骁驎骣驒驔驖驙驦驩驫骺鲠骫骭肮骱骴骶骷髅骾髁髂髄髆膀髇髑髌髋髙髝髞髟髡髣髧髪髫髭髯髲髳髹髺髽髾鬁鬃鬅鬈鬋鬎鬏鬐鬑鬒鬖鬗鬘鬙鬠鬣斗鬫鬬阄鬯鬰鬲鬵鬷魆魈魊魋魍魉魑魖鳔魛魟魣魦魨魬鲂魵魸鮀鲅鮆鲧鲇鲍鲋鮓鲒鲕鮟鱇鮠鮦鮨鲔鲑鮶鮸鮿鲧鯄鯆鲩鯈鲻鯕鲭鲞鯙鯠鲲鯥鲰鲶鳀鯸鳊鲗䲠鹣鳇鰋鳄鳆鰕鰛鰜鲥鰤鳏鰦鳎鳐鳁鳓鰶鲦鲡鰼鰽鱀鱄鳙鱆鳕鱎鱐鳝鳝鳜鲟鲎鱠鳣鱨鲚鱮鱲鱵鱻鲅鳦凫鳯鳲鳷鳻鴂鴃鴄鸩鴈鴎鸰鴔鴗鸳鸯鸲鹆鸱鴠鴢鸪鴥鸸鹋鴳鸻鴷鴽鵀鵁鸺鹁鵖鵙鹈鹕鹅鵟鵩鹌鵫鵵鵷鵻鹍鶂鶊鶏鶒鹙鶗鶡鶤鶦鶬鶱鹟鶵鶸鶹鹡鶿鹚鷁鷃鷄鷇䴘䴘鷊鷏鹧鷕鹥鸷鷞鷟鸶鹪鹩鷩鷫鷭鹇鹇鸴鷾䴙鸂鸇䴙鸏鸑鸒鸓鸬鹳鸜鹂鹸咸鹾麀麂麃麄麇麋麌麐麑麒麚麛麝麤麸面麫麮麯麰麺麾黁黈黉黢黒黓黕黙黝黟黥黦黧黮黰黱黪黶黹黻黼黾鼋鼂鼃鼅鼈鼍鼏鼐鼒冬鼖鼙鼚鼛鼡鼩鼱鼪鼫鼯鼷鼽齁齆齇齈齉齌赍齑龀齕齗龅齚龇齞龃龉龆齢出齧齩齮齯齰齱齵齾厐龑龒龚龖龘龝龡龢龤" + +traditional_characters = "制咖片型超聲盤鑒定仔點他命書歌粉巾字帳恤手指記憶棒形轉彎溝光○〇㐄㐅㐆㐌㐖毒㐜㐡㐤㐰㐺㑇㑳㒳㒸㔾㗂㗎㝵㞎㞙㞞㠯㢲㢴㤅㥁㥯㨗㫺㬎㮎㮚㮸㲋㲱㲾㳮㵎㵪㶸㷖㷭㹢㹴犬㺢狓㺵㼝㽮㿝䍃䔢䖟䖸䗈䗥䗪䝓䠶䥯䦉䯝䰾魚䲔䳗䳘䵹鼄䶑一對應映射丁不識下兒子做二休世丘之貉並中台原則串為甚謂乾淨了百事無成八變五十些人得道雞升天代如併來去個國政策勁幽靈在歐洲遊蕩接樣蘿蔔坑側化傳價元論醇共再准刀兩斷切分耕耘收穫錢貨物向看舊就緒險刻千金動勞永逸匙零夜半卡通回復返影蹤反常態口咬氣句話同吐快吹周味呼諾嗚品紅鍋哄而散起唱和問三知生熟團漆黑火糟堆場空塊麵塌糊塗塵染壁廂夔已足多情露水大早到晚夫妻當關萬莫開失古恨套所料既往孔見提師要家主審寸陰難買鬥牛小撮部陣局展身層巴掌帆風順席地帶過年計於春頭載四季期被蛇怕井繩度願式份彈頃深前律徑心意念差愁孤行俱全房廳交遮打技長把抓死拿眼淚鼻涕鑰鎖折段抿拍即合掃排掬揮撥擁上入擊洞擲攬改故轍敗文值名斑方面旁族日秋餐隔雅里終父旦時晌會霎間晃暴寒曝更月望垠際朝夕本正經利杯羹東西板枝獨秀根筋桿進條龍服務概模次函數又性程總付步腳印趨登毛拔呵氧氮碳決雌雄波未平派謊言流清楚白準溜煙潭有獲聞是處降琴鶴甲病發可拾沙目然瞭直以相眨穿睹瞥瞬矢的解石鳥神教秉虔誠秘種窩蜂窮竅笑置筆苟勾銷抹殺煞等獎箍節吃箭仇雙鵰詩籌籮筐系列紙級士官統絲毫掛維網盡線微吭響股腦胎脈承腔臂力致效資源址器舉功投般說講規貿易葉障著慎滿皆輸號木電池衣傾鐘高低視仁覺醒覽遺角銀幣觸潰九鼎蔽抄出駟馬追重語破貧洗貫走路安蹴至幾蹶振躍役膽汗較輩輪辭贊退六連遍遞邊針血錘音錯門思閃真倒項栽霧類保護川先驚乍體鬨鱗爪鳴滴泡鄰域黨專鼓作齊炒丑烯亥克內酯冬加奴卯肝炎基尺梁街褲鎬客寵庭巳汝昌烷玲磊糖肇酉醛啷青縣韙良香骨鯛丂七集河市弦喜嘴張舌堵區工業姊妹星架構巧彩扭歪拼湊餘熱曜武州爺浮屠美鄉老階樹葷素碎落能魄鰓鰻珠丄丅丆万俟丈尚摸母娘量管群亞虎必我堂令申件裝伏位博俠義界表女墟臺戲臭皮匠勝諸葛亮賽頂倍催請運算包立叉戟離疫苗土史志演圍揭瓦曬夷姑婆帝村寶爛尖杉鹼屜桌山岔島由紀峽壩庫鎮廢從德後拗湯治旬食明昧曹朋友框欄極權冪曲歸依貓民氟硼氯磷鐵江侗自旅法司洋浦梅園溫暖灣焦班幸用田略番疊皇炮捶硝苯酸腺苷稜草鏡穗跳遠索錦綱聚氰胺聯店胚膲愛色堇紫羅蘭芝茶飯菱雲蟲藏藩亂叛蘇親債凳學座恐戀柱測肌腹衩錐係貂企烏跪叩軍車農題迭都甘油屯奏鍵短阿姨陪姐隻顧茅廬槽駕魂鮮鹿頁其菜單乘任供勢午齒漢組織吊調瀉唇坡城報墳外夸將尉建築岸崗公床揚新劍昇杭林栗校樓標款汽社浣海商館劇院鋼華港機械廣媒環球融第醫科證券綜財樂育游漲猶嶺疏癮瞼確兵領導繳肢膛船艾瑟爾蒼蔡虞傚衫覆訪訴課諭議軌述野鉤限敵鞋頜頷顎饒首齦站例修凡劃垂屆屬崽頦廚拜挫擺放旋削棋榻檻禮沉注滑營獄畫确儀聘花葬詔員跌轄週達酒錨閘陷陸雨雪飛威丌于丹久乏予理評產亢卑亦乎舞己悲矩圓詞害誌但住佞佳便俗信票案幅翁倦倫假偏倚斜虧鬼敲停備傷脾胃僅此像儉匱免宜穴焉戴兼容許凍伯仲負彼晝皂軒輊實刊划顛衛戰哥比省非好黃飾別拘束掩奶睬選擇搖擾煩苦枚寫協厭及格受歡迎約只估侵犯割狀告或缺抗拒挽撤救藥喻磨滅端倪少逆逾越避靠適吉譽吝玉含延咎歹聽啻淵善謀均勻堪忍夠太惹妙妥妨孕症孝術室完納推冠積宣疑辯慄碴稱屈撓屑干涉衡待很忙惡忿怎麼怠急恥恭息悅惑惜惟想愉愧怍慌憤啟懂懈懷材才緊招認扣抵拉捨也罷插揣冒搭撞南牆擴核支攻敢雷攀敬裡嗎需景智暇曾罪遇朽枉止況競爭辱求癒渝溶濟左右袒困補爽特寂寞示弱找謝畏強疾徐痛癢冤符眠睦瞅董何厚云措活疲羞者輕玻璃祥兆禁移稂莠穩佛換答簡結果盟絕縷途給談否羈翼耐肖脛毋寧興舒若菲萊痕跡窠臼虛衰臉兔撒鷹棺範該詳諱抬泰讓鬚眉象眾貲賬費灰賴奇慮訓輟辨菽麥辛近送透逞徒速續逮捕遂遑違遜斧鉞艱醉鏽隨觀棄顯飽脂肪使丏丐幫丒且慢末丕替桃宗王尊涼爵各圖屋脊糧署錄壇吾祿職胄襲君廈丗北壑桐疹損逢陵鷸丙寅戌氨腈唑綸辰酮脫氫酶醚丞丟現掉紗帽弄扯砲碗丠両丣坐存激肩臻蒂蓮悖序驅丨丩丫挺杈髻鬟細介俄伊犁京尼布訂普渡央委監察檢查劑圈設警隊斯督剩震境航舶革防托播促質版蠑螈鋒研藝歷殘消頻譜精密製造陲郵候埔堅壓壢凹匯執府究邦俘攝寮彬狼嶽肺腫庸英訊診埋粒胞括控碼韓暑槍樞砥澳哇牟壽甸鑽探篇簽綴縫繼耳肯照婦埃懸璧軸櫃檯辣擱淺邪跑纖阮陽私囊魔丮丰姿采丱燒丳丵丶丷丸參寨朗桂瑞砂衷霞貌鳳僕艦因嫌宰峰幹絡牌持旨祭禱簿編罰賓辦丼丿乀乂乃乄仰慕盛曠留考驗闊乆乇么醜麼乊湖燃乑乒乓乕乖僻忤戾离謬迕乗危肥劫除隙浪婿乙炔腸酰吡咯鹽乚乛乜嘢卿玄宮尾狐龜塔嶷兄弟泉章霄釘耙乞扎哀憐恕討乢乣乤乥乧乨乩童乪乫乭乳暈汁液瑤漿牙癌突竇罩腐膠豬酪蛋糕菌瘤乴乵乶乷乸乹乺乼乾俸冰嘉噦嚎坤媽屍壘旱枯涸俐渴潮澀煸豆燥爹瘦癟癬瞪袋脆薑貝隆餾乿亀亁叫咕攘扔搞男砸竄蓬麻亃亄亅卻亇遲典今臨繁累卵奉婚聰躬巨與遷添裂副宿歲怪噁尕崙愣杆硅硫鈦鈾錳芑雜異鈉砷胂磺琥珀艙棍簧胡茬盜浩盆販郎腿亍洪亐互欠助勉惠操斥諉繫戶譯亓墓碑刑鈴卅渠繽紛斗米旗憲釩燈徽瘟祖拳福穀豐臟腑綁肉醃苓蘊橋鋪霸顏鬧判噴岡底蛙陘礦亖亙亜罕們娜桑那努哈喀弗烈曼松森杜氏盃奧琛敦戊穆聖裔彙薛孫亟亡佚虜羊牢奮釋卷卸契媾感額睫纏誼趾塞擠紐阻還配馳莊亨洛祚亪享津滬畿郊慈菴枇杷膏亭閣鋥麗亳亶亹誅初責翻瘋偶傑叢稠妖拖寰居吸授慧蝸吞壯魅狗矛盾益渣患憂稀描猿夢暫涯畜禍緣沸搜引擎臣橫紜誰混援蒸獸獅稅剖亻亼亽亾什獻剎邡麽仂仃仄仆富怨仈仉畢昔晨殼紹仍仏仒仕宦仗欺恃腰嘆歎炬梓訖施仙后瓊逝仚仝仞仟悔仡佬償填泊拓撲簇羔購頓欽佩髮棻閫馭養億儆尤藉幀賑凌敘帖李柔剛沃眥睚戒訛取饗讀仨仫仮著泳臥躺韶夏裁仳仵唯賢憑釣誕仿似宋彿諷伀碩盼鵝伄儅伈伉儷柯始娃邁戈坦堡帕茨薩廟瑪莉莎藤霍姆伋伍奢胥廷芳豪伎倆侍汛勒希羲雛伐憩整謨閑閒伕伙伴頤伜伝伢叔恆茲恩翰伱伲侶伶俜悧鼬伸懶縮喇叭伹伺伻伽倻輻伾佀佃佇佈喬妮墨佉盧佌貸劣廉昂檔濃矮傘窪緩耗胸谷迷擋率齲宅沫舍療佐貳佑佔優據鏵嘗呢須魯曉佗佘余坪寺瓜銃僧蒙芒陀龕哼嘔坊姦孽弊揖祟繭縛誓賊佝僂瞀佟你奪趕佡佢佣佤佧賈佪佫佯佰佱潔績釀餚佴捲佶佷佸佹佺佻佼佽佾具喚窘壞娛怒慨硬習慣聾膨脹蔓駭貴痺侀侁侂侃侄侅鴻燕侇侈糜靡侉侌妾侏儒倉鼠侐侑侔侖侘侚鏈侜偎傍鈷循柳葫蘆附価侮罵蔑侯岩截蝕侷貼壺嬛宴捷攜桶箋酌俁狹膝狄俅俉俊俏俎俑俓俔諺俚俛黎健呈固墒增守康箱濕祐鏢鑣槓盒靖膜齡俞豹獵噪孚封札筒託衍鴿剪撰稿煉廠禊練繕葺俯瞰撐衝俲俳俴俵俶俷俺俻俾倀倂倅儲卒惶敷猝逃頡蓄崇隱倌倏忽刺蠟燭噍嚼坍扁抽斃蔥楣灌灶糞背藪賣賠閉霉騰倓倔倖倘倜儻倝借箸挹澆閱倡狂倢倣値倥傯倨傲倩匡嗣沖柝珍倬倭寇猩倮倶倷倹勤讚偁偃充偽吏嗓寐惺扮拱芫茜藉虢鈔偈偉晶偌宕距析濾殿疼癱註頗偓偕鴨歇滯偝偟偢忘怡旺偨偩偪偫偭偯偰偱偲偵緝蹄偷減惰漏窺竊偸偺迹傀儡傅傈僳傌籬傎奎琳迪叟芭傒傔傕傖悉荒傜傞傢傣芽逼傭婢傮睨寄檄誦謠頌傴擔辜弓慘蒿悼疤傺傻屄臆巢洩篋羨蓋軋頹傿儸僄僇僉僊働僎僑僔僖僚僝僞僣僤僥僦猴僨僩僬僭僮僯僰僱僵殖籤靜僾僿征隴儁儂儃儇儈朴薄儊儋儌儍儐儓儔儕儗儘儜儞儤儦儩汰哉寡渥裕酷儭儱罐儳儵儹儺儼儽兀臬臲鷲允勛勳宙宵帥憝彞諧嫂鬩暢沛溢盈飢赫兇悍狠猛頑愚妣斬秦遣鞭耀敏榮槃澤爆碟磁禿纜輝霽鹵朵婁孜烽醬勃汀箕裘鉗耶懞蕾徹兌軟遭黜兎児韻媳爸兕觥兗兙兛兜售鍪肚兝兞兟兡兢兣樽殮涅睡稟籍贅泌啡肽奸幕涵澇熵疚眷稃襯訌赴煥椒殲植跏沒試誤猜棲窗肋袖頰兪卦撇鬍岐廓轎疸楓茴瓏廁秩募勺噸寓斤曆畝迫筷釐最淫螺韜兮寬匪篩襄贏軛複兲詐刃堰戎痞蟻餉它冀鑄冂冃円冇冉冊嫁厲礪竭醮冏牧冑冓冔冕冖冗冘冞冢窄抑誣冥冫烘菇蟄冷凝坨橇淇淋炭餅磚磧窖醋雕雹霜冱冶爐艷嘲峻灘淡漠煖颼飲冼冽凃凄愴梗凅凇凈凊凋敝濛凔凜遵汞脢凞几凢処凰凱凵凶焰凸摺刷紋預喪嘍奔巡榜殯芙蓉租籠輯鞘萃凼鋸鑊刁蠻刂娩崩批拆攤掰櫱驟歧顆秒袂贓勿囑忌磋琢膚刈羽刎訟戮舂槳艇刓刖霹靂刜創犢刡恙墅幟筵緻刦刧刨昏默攸尿慾薰潤薰圭刪刮痧鏟刱刲刳刴刵踏磅戳柏槐繡芹莧蝟舟銘鵠鶩刼剁剃辮剄剉履鉛剋剌姻咽哨廊掠桅沿召瞻翅趙卜渺茫郭剒剔剕瀝剚愎毅訥纔剜剝啄採剞剟剡剣剤綵剮腎駛黏剰袍剴紊剷剸剺剽剿劁劂劄劈啪柴扳啦劉奭姥夼昫涓熙禪禹錫翔雁鶚劊劌弩柄蜻蛉劒劓劖劘劙瀾簣賞磯釜晉甜薪逐劦熔紂虐赤囚劬劭労劵効劻劼劾峭艮勅勇勵勍勐臘脖龐漫飼盪粥輒勖勗勘驕餒碌泮雇捐竹騎殊阱勣樸懇謹勦勧勩勯勰勱勲勷勸懲慰誡諫勹芡踐闌匁庇拯粟紮袱裹餃匆遽匈匉匊匋匍匐莖匏匕妝痰膿蛹齋苑烤蹈塘羌熊閥螳螂疆碚竿緯荷茵邙魏匚匜匝匟扶稷匣匭攏匸匹耦匽匾匿卂叮瘡禧軫堤棚迢鈞鍊卄卆遐卉瓷盲瓶噹胱腱裸卋卌卍卐怯污賤鄙齷齪陋卓溪唐梯漁陳棗泥漳潯澗梨芬譙贍轅迦鄭単驢弈洽鰲卛占筮卝卞卟吩啉屎翠厄卣卨卪卬卮榫襖璽綬鈕蚤懼殆篤聳卲帘帙繞卹卼卽厂厎厓厔厖厗奚厘厙厜厝諒厠厤厥厪膩孢厮厰厳厴厹厺粕垢蕪菁厼厾叁悟茸薯叄吵笄悌哺譏坫壟弧芯杠潛嬰芻袁詰貪諜煽饋駁収岳締災賄騙叚叡吻攔蘑蜜訣燧玩硯箏椎藺銅逗驪另覓叨嘮謁杵姓喊嚷囂咚嚀塑尋惱憎擦祇泣滲蝠叱吒咄咤喝籀黛舵舷叵叶鐸懿昭穰苴遼叻叼吁塹嫖賭瞧爬衆抒吅吆夥巹橡滌抱縱摩郡唁墜扇籃膀襪頸吋愾諮酬哭妓媛暗錶韁邇妃羿絮蕃渾拐葵暮隅吔吖啶嗪戚吜嗇噬嚥吟哦詠吠吧唧嗒咐吪雋咀徵燐苞茹鈣哧吮吰吱嘎吲哚吳棟嬌窟孟簫忠晗淞闔閭趼宇吶睛噓拂捧疵熄竽笛糠吼吽呀呂韋矇呃呆笨呇貢呉罄呋喃呎呏呔呠呡癡呣呤呦呧瑛眩扒晬淑姬瑜璇鵑呪呫嗶嚅囁呬呯呰呱呲咧噌鈍呴呶呷呸呺呻哱咻嘯嚕籲坎坷邏呿咁咂咆哮咇咈咋蟹煦珅藹咍咑咒詛咔噠嚓咾噥哩喱咗咠咡咢咣咥咦咨嗟詢咩咪咫嚙齧咭咮咱咲咳嗆嗽咴咷咸咹咺咼喉咿婉慟憫賦矜綠茗藍哂搶瞞哆嗦囉噻啾濱彗哋哌哎唷喲哏哐哞哢哤哪裏哫啼喘哰哲萎蚌哳哶哽哿唄唅唆唈唉唎唏嘩堯棣殤璜睿肅唔睇唕唚唞唣喳唪唬唰喏唲唳唵嘛唶唸唹唻唼唾唿啁啃鸚鵡啅埠棧榷祺舖鞅飆啊啍啎啐啓啕啖啗啜啞祈啢啣啤啥啫啱啲啵啺饑啽噶崑沁喁喂喆裙喈嚨喋喌喎喑喒喓喔粗喙幛慶滋鵲喟喣喤喥喦喧騷喨喩梆喫葡萄喭駝挑嚇碰樅瓣純皰藻趟鉻喵営喹喺喼喿嗀嗃嗄嗅嗈嗉嗊嗍嗐嗑嗔詬嗕嗖嗙嗛嗜痂癖嗝嗡嗤嗥嗨嗩嗬嗯嗰嗲嗵嘰嗷嗹嗾嗿嘀嘁嘂嘅惋嘈峪禾蔭嘊嘌嘏嘐嘒嘓嘖嘚嘜嘞嘟囔嘣嘥嘦嘧嘬嘭這謔嚴敞饞鬆嘵嘶嘷嘸蝦嘹嘻嘽嘿噀噂噅噇噉噎噏噔噗噘噙噚噝噞噢噤蟬皿噩噫噭噯噱噲噳嚏涌灑欲巫霏噷噼嚃嚄嚆抖嚌嚐嚔囌嚚嚜嚞嚟嚦嚬嚭嚮嚯嚲嚳飭按竣苛嚵嚶囀囅囈膪謙囍囒囓囗囘蕭酚飄濺諦囝溯眸紇鑾鶻囟殉囡団囤囥囧囨囪囫圇囬囮囯囲図囶囷囸囹圄圉擬囻囿圀圂圃圊粹蠹赦圌墾圏滾鯡鑿枘圕圛圜圞坯埂壤骸炕祠窯豚紳魠鯪鱉圧握圩圪垯圬圮圯炸岬幔毯祇窨菩溉圳圴圻圾坂坆沾坋坌舛壈昆墊墩椅坒坓坩堝坭坰坱坳坴坵坻坼楊掙涎簾垃垈垌垍垓垔垕垗垚垛垝垣垞垟垤垧垮垵垺垾垿埀畔埄埆埇埈埌殃隍埏埒埕埗埜埡埤埦埧埭埯埰埲埳埴埵埶紼埸培怖樁礎輔埼埽堀訶姪廡堃堄摧磐貞韌砌堈堉堊堋堌堍堎堖堙堞堠礁堧堨輿堭堮蜓摘堲堳堽堿塁塄塈煤塋棵塍塏塒塓綢塕鴉沽虱塙塚塝繆塡塢塤塥塩塬塱塲蟎塼塽塾塿墀墁墈墉墐夯増毀墝墠墦漬缽墫墬墮墰墺墻櫥壅壆壊壌壎壒榨蒜壔壕壖壙壚壜壝壠壡壬壭壱売壴壹壻壼寢壿夂夅夆変夊夌漱邑夓腕泄甥禦骼夗夘夙袞瑙妊娠醣梟珊鶯鷺戧幻魘夤蹀祕擂鶇姚宛閨嶼庾撻拇賛蛤裨菠氅漓撈湄蚊霆鯊箐篆篷荊肆舅荔鮃巷慚骰辟邱鎔鐮阪漂燴鯢鰈鱷鴇臚鵬妒峨譚枰晏璣癸祝秤竺牡籟恢罡螻蠍賜絨御梭夬夭砣榆怙枕夶夾餡奄崛葩譎奈賀祀贈奌奐奓奕訢詝奘奜奠奡奣陶奨奩魁奫奬奰媧孩貶隸酥宄狡猾她奼嫣妁氈荼皋膻蠅嬪妄妍嫉媚嬈妗趣妚妞妤礙妬婭妯娌妲妳妵妺姁姅姉姍姒姘姙姜姝姞姣姤姧姫姮娥姱姸姺姽婀娀誘懾脅娉婷娑娓娟娣娭娯娵娶娸娼婊婐婕婞婤婥谿孺婧婪婬婹婺婼婽媁媄媊媕媞媟媠媢媬媮媯媲媵媸媺媻媼眯媿嫄嫈嫋嫏嫕嫗嫘嫚嫜嫠嫡嫦嫩嫪毐嫫嫬嫰嫵嫺嫻嫽嫿嬀嬃嬅嬉耍嬋痴豔嬔嬖嬗嬙嬝嬡嬢嬤嬦嬬嬭幼嬲嬴嬸嬹嬾嬿孀孃孅孌孏曰癲屏孑孓雀孖斟簍謎摺孛矻鳩崮軻祜鸞孥邈毓棠臏孬孭孰孱孳孵泛罔銜孻孿宀宁宂拙株薇掣撫琪瓿榴謐彌宊濂祁瑕宍宏碁宓邸讞実潢町宥宧宨宬徵崎駿掖闕臊煮禽蠶宸豫寀寁寥寃簷庶寎暄磣寔寖寘寙寛寠苫寤肘洱濫蒗陝覈寪弘綽螽寳擅疙瘩晷対檐専尃尅贖絀繚疇釁尌峙醌襟痲碧屁昊槌淘恵瀑牝畑莓缸羚覷蔻髒躁尒尓銳尗尙尜尟尢尥尨尪尬尭尰擒尲尶尷尸尹潽蠖蛾尻釦梢蚴鰭脬蹲屇屌蚵屐屓挪屖屘屙屛屝屢屣巒嶂巖舄屧屨屩屪屭屮戍駐鉀崖嵛巔旮旯楂欖櫸芋茱萸靛麓屴屹屺屼岀岊岌岍阜岑彭鞏岒岝岢嵐岣岧岨岫岱岵岷峁峇峋峒峓峞峠嵋峩峯峱峴峹峿崀崁崆禎崋崌崍嶇崐崒崔嵬巍螢顥崚崞崟崠崢巆崤崦崧殂崬崱崳崴崶崿嵂嵇嵊泗嵌嵎嵒嵓嵗嵙嵞嵡嵩嵫嵯嵴嵼嵾嶁嶃嶄晴嶋嶌嶒嶓嶔嶗嶙嶝嶞嶠嶡嶢嶧嶨嶭嶮嶰嶲嶴嶸巂巃巇巉巋巌巓巘巛滇芎巟巠弋迴巣巤炊擘蜥蟒蠱覡巰蜀彥淖杏茂甫楞巻巽幗巿帛斐鯽蕊帑帔帗帚琉汶帟帡帣帨帬帯帰帷帹暆幃幄幇幋幌幏幘幙幚幞幠幡幢幦幨幩幪幬幭幯幰遙蹉跎餘庚鑑幵幷稚邃庀庁広庄庈庉笠庋跋庖犧庠庤庥鯨庬庱庳庴庵馨衢庹庿廃廄廆廋廌廎廏廐廑廒廕廖廛廝搏鑼廞弛袤廥廧廨廩廱綿踵髓廸廹甌鄴廻廼廾廿躔弁皺弇弌弍弎弐弒弔詭憾薦弝弢弣弤弨弭弮弰弳霖繇燾斌旭溥騫弶弸弼弾彀彄彆纍糾彊彔彖彘彟彠陌彤貽彧繪虹彪炳彫蔚鷗彰癉彲彳彴彷彷徉徨彸彽踩斂旆徂徇徊渭畬鉉裼従筌徘徙徜徠膳甦萌漸徬徭醺徯徳徴潘徻徼忀瘁胖燎怦悸顫扉犀澎湃砰恍惚絞隘忉憚挨餓忐忑忒忖応忝忞耿忡忪忭忮忱忸怩忻悠懣怏遏怔怗怚怛怞懟黍訝怫怭懦怱怲怳怵惕怸怹恁恂恇恉恌恏恒恓恔恘恚恛恝恞恟恠恣恧眄恪恫恬澹恰恿悀悁悃悄悆悊悐悒晦悚悛悜悝悤您悩悪悮悰悱悽惻悳悴悵惘悶悻悾惄愫鍾蒐惆惇惌惎惏惓惔惙惛耄惝瘧濁惥惦惪惲惴惷惸拈愀愃愆愈愊愍愐愑愒愓愔愕愙氓蠢騃昵愜赧愨愬愮愯愷愼慁慂慅慆慇靄慉慊慍慝慥慪慫慬慱慳慴慵慷慼焚憀灼鬱憃憊憋憍眺捏軾憒憔憖憙憧憬憨憪憭憮憯憷憸憹憺懃懅懆邀懊懋懌懍懐懞懠懤懥懨懫懮懰懱毖懵遁樑雍懺懽戁戄戇戉戔戕戛戝戞戠戡戢戣戤戥戦戩戭戯轟戱披菊牖戸戹戺戻戼戽鍬扂楔扃扆扈扊杖牽絹銬鐲賚扐摟攪烊盹瞌跟躉鑔靶鼾払扗玫腮扛扞扠扡扢盔押扤扦扱罾揄綏鞍郤窾扻扼扽抃抆抈抉抌抏瞎抔繯縊擻抜抝択抨摔歉躥牾抶抻搐泵菸拃拄拊髀拋拌脯拎拏拑擢秧沓曳攣迂拚拝拠拡拫拭拮踢拴拶拷攢拽掇芥橐簪摹疔挈瓢驥捺蹻挌挍挎挐揀挓挖掘浚挙揍聵挲挶挾挿捂捃捄捅捆捉捋胳膊揎捌捍捎軀蛛捗捘捙捜捥捩捫捭据捱捻捼捽掀掂掄臀膘掊掎掏掐笙掔掗掞棉芍掤搪闡掫掮掯揉掱掲掽掾揃揅揆搓揌諢揕揗揘揜揝揞揠揥揩揪揫櫫遒麈揰揲揵揶揸揹揺搆搉搊搋搌搎搔搕撼櫓搗搘搠搡搢搣搤搥搦搧搨搬楦褳訕赸搯搰搲搳搴搵搷搽搾搿摀摁摂摃摎摑摒摓跤摙摛摜摞摠摦睺羯摭摮摯摰摲摳摴摶摷摻摽撂撃撅稻撊撋撏鐧潑撕撙撚撝撟撢撣撦撧撩撬撱朔撳蚍蜉撾撿擀擄闖擉缶觚擐擕擖擗擡擣擤澡腚擧擨擩擫擭擯擰擷擸擼擽擿攃攄攆攉攥攐攓攖攙攛每攩攫轡澄攮攰攲攴軼攷砭訐攽碘敁敃敇敉敍敎筏敔敕敖閏誨敜煌敧敪敱敹敺敻敿斁衽斄牒縐謅斉斎斕鶉讕駮鱧斒筲斛斝斞斠斡斢斨斫斮晾沂潟穎絳邵斲斸釳於琅斾斿旀旂旃旄渦旌旎旐旒旓旖旛旝旟旡旣浴旰獺魃旴旹旻旼旽昀昃昄昇昉晰躲澈熹皎皓礬昑昕昜昝昞昡昤暉筍昦昨昰昱昳昴昶昺昻晁蹇隧蔬髦晄晅晒晛晜晞晟晡晢晤晥曦晩萘瑩顗晿暁暋暌暍暐暔暕煅暘暝暠暡曚暦暨暪朦朧暱暲殄馮暵暸暹暻暾曀曄曇曈曌曏曐曖曘曙曛曡曨曩駱曱甴肱曷牘禺錕曽滄耽朁朅朆杪栓誇竟粘絛朊膺朏朐朓朕朘朙瞄覲溘饔飧朠朢朣柵椆澱蝨朩朮朰朱炆璋鈺熾鹮朳槿朶朾朿杅杇杌隉欣釗湛漼楷瀍煜玟纓翱肈舜贄适逵杓杕杗杙荀蘅杝杞脩珓筊杰榔狍閦顰緬莞杲杳眇杴杶杸杻杼枋枌枒枓衾葄翹紓逋枙狸椏枟槁枲枳枴枵枷枸櫞枹枻柁柂柃柅柈柊柎某柑橘柒柘柙柚柜柞櫟柟柢柣柤柩柬柮柰柲橙柶柷柸柺査柿栃栄栒栔栘栝栟栢栩栫栭栱栲栳栴檀栵栻桀驁桁鎂桄桉桋桎梏椹葚桓桔桕桜桟桫欏桭桮桯桲桴桷桹湘溟梃梊梍梐潼梔梘梜梠梡梣梧梩梱梲梳梴梵梹棁棃櫻棐棑棕櫚簑繃蓑棖棘棜棨棩棪棫棬棯棰棱棳棸棹槨棼椀椄苕椈椊椋椌椐椑椓椗検椤椪椰椳椴椵椷椸椽椿楀楄楅篪楋楍楎楗楘楙楛楝楟楠楢楥楨楩楪楫楬楮楯楰楳楸楹楻楽榀榃榊榎槺榕榖榘榛狉莽榜笞榠榡榤榥榦榧榪榭榰榱槤霰榼榾榿槊閂槎槑槔槖様槜槢槥槧槪槭槮槱槲槻槼槾樆樊樏樑樕樗樘樛樟樠樧樨権樲樴樵猢猻樺樻罍樾樿橁橄橆橈笥龠橕橚橛輛橢橤橧豎膈跨橾橿檁檃檇檉檍檎檑檖檗檜檟檠檣檨檫檬檮檳檴檵檸櫂櫆櫌櫛櫜櫝櫡櫧櫨櫪櫬櫳櫹櫺茄櫽欀欂欃欐欑欒欙欞溴欨欬欱欵欶欷歔欸欹欻欼欿歁歃歆艎歈歊蒔蝶歓歕歘歙歛歜歟歠蹦詮鑲蹣跚陞陟歩歮歯歰歳歴璞歺瞑歾歿殀殈殍殑殗殜殙殛殞殢殣殥殪殫殭殰殳荃殷殸殹蛟殻殽謗毆毈毉餵毎毑蕈毗毘毚茛鄧毧毬毳毷毹毽毾毿氂氄氆靴氉氊氌氍氐聊氕氖気氘氙氚氛氜氝氡洶焊痙氤氳氥氦鋁鋅氪烴氬銨痤汪滸漉痘盂碾菖蒲蕹蛭螅氵氷氹氺氽燙氾氿渚汆汊汋汍汎汏汐汔汕褟汙汚汜蘺沼穢衊汧汨汩汭汲汳汴隄汾沄沅沆瀣沇沈葆浸淪湎溺痼痾沌沍沏沐沔沕沘浜畹礫沚沢沬沭沮沰沱灢沴沷籽沺烹濡洄泂肛泅泆湧肓泐泑泒泓泔泖泙泚泜泝泠漩饃濤粼濘蘚鰍泩泫泭泯銖泱泲洇洊涇琵琶荽薊箔洌洎洏洑潄濯洙洚洟洢洣洧洨洩痢滔洫洮洳洴洵洸洹洺洼洿淌蜚浄浉浙贛渫浠浡浤浥淼瀚浬浭翩萍浯浰蜃淀苔蛞蝓蜇螵蛸煲鯉浹浼浽溦涂涊涐涑涒涔滂涖涘涙涪涫涬涮涴涶涷涿淄淅淆淊淒黯淓淙漣淜淝淟淠淢淤淥淦淩猥藿褻淬淮淯淰淳詣淶紡淸淹燉癯綺渇済渉渋渓渕渙渟渢滓渤澥渧渨渮渰渲渶渼湅湉湋湍湑湓湔黔湜湝湞湟湢湣湩湫湮麟湱湲湴湼満溈溍溎溏溛舐漭溠溤溧馴溮溱溲溳溵溷溻溼溽溾滁滃滉滊滎滏稽滕滘滙滝滫滮羼耷滷滹滻煎漈漊漎繹漕漖漘漙漚漜漪漾漥漦漯漰漵漶漷濞潀潁潎潏潕潗潚潝潞潠潦祉瘍潲潵潷潸潺潾潿澁澂澃澉澌澍澐澒澔澙澠澣澦澧澨澫澬澮澰澴澶澼熏郁濆濇濈濉濊貊濔疣濜濠濩觴濬濮盥濰濲濼瀁瀅瀆瀋瀌瀏瀒瀔瀕瀘瀛瀟瀠瀡瀦瀧瀨瀬瀰瀲瀳瀵瀹瀺瀼灃灄灉灋灒灕灖灝灞灠灤灥灨灩灪蜴灮燼獴灴灸灺炁炅魷炗炘炙炤炫疽烙釺炯炰炱炲炴炷燬炻烀烋瘴鯧烓烔焙烜烝烳飪烺焃焄耆焌焐焓焗焜焞焠焢焮焯焱焼煁煃煆煇煊熠煍熬煐煒煕煗燻礆霾煚煝煟煠煢矸煨瑣煬萁煳煺煻熀熅熇熉羆熒穹熗熘熛熜稔諳爍熤熨熯熰眶螞熲熳熸熿燀燁燂燄盞燊燋燏燔隼燖燜燠燡燦燨燮燹燻燽燿爇爊爓爚爝爟爨蟾爯爰爲爻爿爿牀牁牂牄牋牎牏牓牕釉牚腩蒡虻牠雖蠣牣牤牮牯牲牳牴牷牸牼絆牿靬犂犄犆犇犉犍犎犒犖犗犛犟犠犨犩犪犮犰狳犴犵犺狁甩狃狆狎狒獾狘狙黠狨狩狫狴狷狺狻豕狽蜘猁猇猈猊猋猓猖獗猗猘猙獰獁猞猟獕猭猱猲猳猷猸猹猺玃獀獃獉獍獏獐獒獘獙獚獜獝獞獠獢獣獧鼇蹊獪獫獬豸獮獯鬻獳獷獼玀玁菟玅玆玈珉糝禛郅玍玎玓瓅玔玕玖玗玘玞玠玡玢玤玥玦玨瑰玭玳瑁玶玷玹玼珂珇珈瑚珌饈饌珔珖珙珛珞珡珣珥珧珩珪珮珶珷珺珽琀琁隕琊琇琖琚琠琤琦琨琫琬琭琮琯琰琱琲瑯琹琺琿瑀瑂瑄瑉瑋瑑瑔瑗瑢瑭瑱瑲瑳瑽瑾瑿璀璨璁璅璆璈璉璊璐璘璚璝璟璠璡璥璦璩璪璫璯璲璵璸璺璿瓀瓔瓖瓘瓚瓛臍瓞瓠瓤瓧瓩瓮瓰瓱瓴瓸瓻瓼甀甁甃甄甇甋甍甎甏甑甒甓甔甕甖甗飴蔗甙詫鉅粱盎銹糰甡褥産甪甬甭甮甯鎧甹甽甾甿畀畁畇畈畊畋畎畓畚畛畟鄂畤畦畧荻畯畳畵畷畸畽畾疃疉疋疍疎簞疐疒疕疘疝疢疥疧疳疶疿痁痄痊痌痍痏痐痒痔痗瘢痚痠痡痣痦痩痭痯痱痳痵痻痿瘀瘂瘃瘈瘉瘊瘌瘏瘐瘓瘕瘖瘙瘚瘛瘲瘜瘝瘞瘠瘥瘨瘭瘮瘯瘰癧瘳癘瘵瘸瘺瘻瘼癃癆癇癈癎癐癔癙癜癠癤癥癩蟆癪癭癰発踔紺蔫酵皙砬砒翎翳蘞鎢鑞皚鵯駒鱀粵褶皀皁莢皃鎛皈皌皐皒硃皕皖皘皜皝皞皤皦皨皪皫皭糙綻皴皸皻皽盅盋盌盍盚盝踞盦盩鞦韆盬盭眦睜瞤盯盱眙裰盵盻睞眂眅眈眊県眑眕眚眛眞眢眣眭眳眴眵眹瞓眽郛睃睅睆睊睍睎睏睒睖睙睟睠睢睥睪睪睯睽睾瞇瞈瞋瞍逛瞏瞕瞖瞘瞜瞟瞠瞢瞫瞭瞳瞵瞷瞹瞽闍瞿矓矉矍鑠矔矗矙矚矞矟矠矣矧矬矯矰矱硪碇磙罅舫阡、矼矽礓砃砅砆砉砍砑砕砝砟砠砢砦砧砩砫砮砳艏砵砹砼硇硌硍硎硏硐硒硜硤硨磲茚鋇硭硻硾碃碉碏碣碓碔碞碡碪碫碬碭碯碲碸碻礡磈磉磎磑磔磕磖磛磟磠磡磤磥蹭磪磬磴磵磹磻磽礀礄礅礌礐礚礜礞礤礧礮礱礲礵礽礿祂祄祅祆禳祊祍祏祓祔祕祗祘祛祧祫祲祻祼餌臠錮禂禇禋禑禔禕隋禖禘禚禜禝禠禡禢禤禥禨禫禰禴禸稈秈秊闈颯秌秏秕笈蘵賃秠秣秪秫秬秭秷秸稊稌稍稑稗稙稛稞稬稭稲稹稼顙稾穂穄穇穈穉穋穌貯穏穜穟穠穡穣穤穧穨穭穮穵穸窿闃窀窂窅窆窈窕窊窋窌窒窓窔窞窣窬黷蹙窰窳窴窵窶窸窻竁竃竈竑竜竝竦竪篦篾笆鮫竾笉笊笎笏笐靨笓笤籙笪笫笭笮笰笱笲笳笵笸笻筀筅筇筈筎筑筘筠筤筥筦筧筩筭筯筰筱筳筴讌筸箂箇箊箎箑箒箘箙箛箜篌箝箠箬鏃箯箴箾篁篔簹篘篙篚篛篜篝篟篠篡篢篥篧篨篭篰篲篳篴篶篹篼簀簁簃簆簉簋簌簏簜簟簠簥簦簨簬簰簸簻籊籐籒籓籔籖籚籛籜籣籥籧籩籪籫籯芾麴籵籸籹籼粁粃粋粑粔糲粛粞粢粧粨粲粳粺粻粽闢粿糅糆糈糌糍糒糔萼糗蛆蹋糢糨糬糭糯糱糴糶糸糺紃蹼鰹黴紆紈絝紉閩襻紑紕紘錠鳶鷂紝紞紟紥紩紬紱紲紵紽紾紿絁絃絅経絍絎絏縭褵絓絖絘絜絢絣螯絪絫聒絰絵絶絺絻絿綀綃綅綆綈綉綌綍綎綑綖綘継続緞綣綦綪綫綮綯綰罟蝽綷縩綹綾緁緄緅緆緇緋緌緎総緑緔緖緗緘緙緜緡緤緥緦纂緪緰緱緲緶緹縁縃縄縈縉縋縏縑縕縗縚縝縞縟縠縡縢縦縧縯縰騁縲縳縴縵縶縹縻衙縿繄繅繈繊繋繐繒繖繘繙繠繢繣繨繮繰繸繻繾纁纆纇纈纉纊纑纕纘纙纚纛缾罃罆罈罋罌罎罏罖罘罛罝罠罣罥罦罨罫罭鍰罳罶罹罻罽罿羂羃羇羋蕉51鴕羑羖羗羜羝羢羣羥羧羭羮羰羱羵羶羸藜鮐翀翃翄翊翌翏翕翛翟翡翣翥翦躚翪翫翬翮翯翺翽翾翿闆饕鴰鍁耋耇耎耏耑耒耜耔耞耡耤耨耩耪耬耰鬢耵聹聃聆聎聝聡聦聱聴聶聼閾聿肄肏肐肕腋肙肜肟肧胛肫肬肭肰肴肵肸肼胊胍胏胑胔胗胙胝胠銓胤胦胩胬胭胯胰胲胴胹胻胼胾脇脘脝脞脡脣脤脥脧脰脲脳腆腊腌臢腍腒腓腖腜腠腡腥腧腬腯踝蹬鐐腴腶蠕誹膂膃膆膇膋膔膕膗膙膟黐膣膦膫膰膴膵膷膾臃臄臇臈臌臐臑臓臕臖臙臛臝臞臧蓐詡臽臾臿舀舁鰟鮍舋舎舔舗舘舝舠舡舢舨舭舲舳舴舸舺艁艄艅艉艋艑艕艖艗艘艚艜艟艣艤艨艩艫艬艭荏艴艶艸艹艻艿芃芄芊萰陂藭芏芔芘芚蕙芟芣芤茉芧芨芩芪芮芰鰱芴芷芸蕘豢芼芿苄苒苘苙苜蓿苠苡苣蕒苤苧苪鎊苶苹苺苻苾茀茁范蠡萣茆茇茈茌茍茖茞茠茢茥茦菰茭茯茳藨茷藘茼荁荄荅荇荈菅蜢鴞荍荑荘荳荵荸薺莆莒莔莕莘莙莚莛莜莝莦莨菪莩莪莭莰莿菀菆菉菎菏菐菑菓菔菕菘菝菡菢菣菥蓂菧菫轂鎣菶菷菹醢菺菻菼菾萅萆萇萋萏萐萑萜萩萱萴萵萹萻葇葍葎葑葒葖葙葠葥葦葧葭葯葳葴葶葸葹葽蒄蒎蒓蘢薹蒞蒟蒻蒢蒦蒨蒭藁蒯蒱鉾蒴蒹蒺蒽蓀蓁蓆蓇蓊蓌蓍蓏蓓蓖蓧蓪蓫蓽跣藕蓯蓰蓱蓴蓷蓺蓼蔀蔂蔃蔆蔇蔉蔊蔋蔌蔎蔕蔘蔙蔞蔟鍔蔣雯蔦蔯蔳蔴蔵蔸蔾蕁蕆蕋蕍蕎蕐蕑蕓蕕蕖蕗蕝蕞蕠蕡蕢蕣蕤蕨蕳蕷蕸蕺蕻薀薁薃薅薆薈薉薌薏薐薔薖薘薙諤釵薜薠薢薤薧薨薫薬薳薶薷薸薽薾薿藄藇藋藎藐藙藚藟藦藳藴藶藷藾蘀蘁蘄蘋蘗蘘蘝蘤蘧蘩蘸蘼虀虆虍蟠虒虓虖虡虣虥虩虯虰蛵虵虷鱒虺虼蚆蚈蚋蚓蚔蚖蚘蚜蚡蚣蚧蚨蚩蚪蚯蚰蜒蚱蚳蚶蚹蚺蚻蚿蛀蛁蛄蛅蝮蛌蛍蛐蟮蛑蛓蛔蛘蛚蛜蛡蛣蜊蛩蛺蛻螫蜅蜆蜈蝣蜋蜍蜎蜑蠊蜛餞蜞蜣蜨蜩蜮蜱蜷蜺蜾蜿蝀蝃蝋蝌蝍蝎蝏蝗蝘蝙蝝鱝蝡蝤蝥蝯蝰蝱蝲蝴蝻螃蠏螄螉螋螒螓螗螘螙螚蟥螟螣螥螬螭螮螾螿蟀蟅蟈蟊蟋蟑蟓蟛蟜蟟蟢蟣蟨蟪蟭蟯蟳蟶蟷蟺蟿蠁蠂蠃蠆蠋蠐蠓蠔蠗蠙蠚蠛蠜蠧蠨蠩蠭蠮蠰蠲蠵蠸蠼蠽衁衂衄衇衈衉衋衎衒衕衖衚衞裳鈎衭衲衵衹衺衿袈裟袗袚袟袢袪袮袲袴袷袺袼褙袽裀裉裊裋裌裍裎裒裛裯裱裲裴裾褀褂褉褊褌褎褐褒褓褔褕褘褚褡褢褦褧褪褫褭褯褰褱襠褸褽褾襁襃襆襇襉襋襌襏襚襛襜襝襞襡襢襤襦襫襬襭襮襴襶襼襽襾覂覃覅覇覉覊覌覗覘覚覜覥覦覧覩覬覯覰観覿觔觕觖觜觽觝觡酲觩觫觭觱觳觶觷觼觾觿言賅訃訇訏訑訒詁託訧訬訳訹証訾詀詅詆譭詈詊詎詑詒詖詗詘詧詨詵詶詸詹詻詼詿誂誃誄鋤誆誋誑誒誖誙誚誥誧説読誯誶誾諂諄諆諌諍諏諑諕諗諛諝諞諟諠諡諴諵諶諼謄謆謇謌謍謏謑謖謚謡謦謪謫謳謷謼謾譁譅譆譈譊譌譒譔譖鑫譞譟譩譫譬譱譲譴譸譹譾讅讆讋讌讎讐讒讖讙讜讟谽豁豉豇豈豊豋豌豏豔豞豖豗豜豝豣豦豨豭豱豳豵豶豷豺豻貅貆貍貎貔貘貙貜貤饜貰餸貺賁賂賏賒賕賙賝賡賧賨賫鬭賮賵賸賺賻賾贇贉贐贔贕贗赬赭赱赳迄趁趂趄趐趑趒趔趡趦趫趮趯趲趴趵趷趹趺趿跁跂跅跆躓蹌跐跕跖跗跙跛跦跧跩跫跬跮跱跲跴跺跼跽踅踆踈踉踊踒踖踘踜踟躇躕踠踡踣踤踥踦踧蹺踫踮踰踱踴踶踹踺踼踽躞蹁蹂躪蹎蹐蹓蹔蹕蹚蹜蹝蹟蹠蹡蹢躂蹧蹩蹪蹯鞠蹽躃躄躅躊躋躐躑躒躘躙躛躝躠躡躦躧躩躭躰躳躶軃軆輥軏軔軘軜軝齶転軥軨軭軱軲轆軷軹軺軽軿輀輂輦輅輇輈輓輗輙輜輞輠輤輬輭輮輳輴輵輶輹輼輾轀轇轏轑轒轔轕轖轗轘轙轝轞轢轤辠辢辤辵辶辺込辿迅迋迍麿迓迣迤邐迥迨迮迸迺迻迿逄逅逌逍逑逓逕逖逡逭逯逴逶逹遄遅遉遘遛遝遢遨遫遯遰遴遶遹遻邂邅邉邋邎邕邗邘邛邠邢邧邨邯鄲邰邲邳邴邶邷邽邾邿郃郄郇郈郔郕郗郙郚郜郝郞郟郠郢郪郫郯郰郲郳郴郷郹郾郿鄀鄄鄆鄇鄈鄋鄍鄎鄏鄐鄑鄒鄔鄕鄖鄗鄘鄚鄜鄞鄠鄢鄣鄤鄦鄩鄫鄬鄮鄯鄱鄶鄷鄹鄺鄻鄾鄿酃酅酆酇酈酊酋酎酏酐酣酔酕醄酖酗酞酡酢酤酩酴酹酺醁醅醆醊醍醐醑醓醖醝醞醡醤醨醪醭醯醰醱醲醴醵醸醹醼醽醾釂釃釅釆釈鱸鎦閶釓釔釕鈀釙鼢鼴釤釧釪釬釭釱釷釸釹鈁鈃鈄鈆鈇鈈鈊鈌鈐鈑鈒鈤鈥鈧鈬鈮鈰鈳鐺鈸鈹鈽鈿鉄鉆鉈鉋鉌鉍鉏鉑鉕鉚鉢鉥鉦鉨鉬鉭鉱鉲鉶鉸鉺鉼鉿銍銎銑銕鏤銚銛銠銣銤銥銦銧銩銪銫銭銰銲銶銻銼銾鋂鋃鋆鋈鋊鋌鋍鋏鋐鋑鋕鋘鋙鋝鋟鋦鋨鋩鋭鋮鋯鋰鋱鋳鋹鋺鋻鏰鐱錀錁錆錇錈錍錏錒錔錙錚錛錞錟錡錤錩錬録錸錼鍀鍆鍇鍉鍍鍏鍐鍘鍚鍛鍠鍤鍥鍩鍫鍭鍱鍴鍶鍹鍺鍼鍾鎄鎇鎉鎋鎌鎍鎏鎒鎓鎗鎘鎚鎞鎡鎤鎩鎪鎭鎯鎰鎳鎴鎵鎸鎹鎿鏇鏊鏌鏐鏑鏖鏗鏘鏚鏜鏝鏞鏠鏦鏨鏷鏸鏹鏻鏽鏾鐃鐄鐇鐏鐒鐓鐔鐗馗鐙鐝鐠鐡鐦鐨鐩鐫鐬鐱鐳鐶鐻鐽鐿鑀鑅鑌鑐鑕鑚鑛鑢鑤鑥鑪鑭鑯鑱鑴鑵鑷钁钃镻閆閈閌閎閒閔閗閟閡関閤閤閧閬閲閹閺閻閼閽閿闇闉闋闐闑闒闓闘闚闞闟闠闤闥阞阢阤阨阬阯阹阼阽陁陑陔陛陜陡陥陬騭陴険陼陾隂隃隈隒隗隞隠隣隤隩隮隰顴隳隷隹雂雈雉雊雎雑雒雗雘雚雝雟雩雰雱驛霂霅霈霊霑霒霓霙霝霢霣霤霨霩霪霫霮靁靆靉靑靚靣靦靪靮靰靳靷靸靺靼靿鞀鞃鞄鞌鞗鞙鞚鞝鞞鞡鞣鞨鞫鞬鞮鞶鞹鞾韃韅韉馱韍韎韔韖韘韝韞韡韣韭韮韱韹韺頀颳頄頇頊頍頎頏頒頖頞頠頫頬顱頯頲頴頼顇顋顑顒顓顔顕顚顜顢顣顬顳颭颮颱颶颸颺颻颽颾颿飀飂飈飌飜飡飣飤飥飩飫飮飱飶餀餂餄餎餇餈餑餔餕餖餗餚餛餜餟餠餤餧餩餪餫餬餮餱餲餳餺餻餼餽餿饁饅饇饉饊饍饎饐饘饟饢馘馥馝馡馣騮騾馵馹駃駄駅駆駉駋駑駓駔駗駘駙駜駡駢駪駬駰駴駸駹駽駾騂騄騅騆騉騋騍騏驎騑騒験騕騖騠騢騣騤騧驤騵騶騸騺驀驂驃驄驆驈驊驌驍驎驏驒驔驖驙驦驩驫骺鯁骫骭骯骱骴骶骷髏骾髁髂髄髆髈髐髑髕髖髙髝髞髟髡髣髧髪髫髭髯髲髳髹髺髽髾鬁鬃鬅鬈鬋鬎鬏鬐鬑鬒鬖鬗鬘鬙鬠鬣鬪鬫鬬鬮鬯鬰鬲鬵鬷魆魈魊魋魍魎魑魖鰾魛魟魣魦魨魬魴魵魸鮀鮁鮆鮌鮎鮑鮒鮓鮚鮞鮟鱇鮠鮦鮨鮪鮭鮶鮸鮿鯀鯄鯆鯇鯈鯔鯕鯖鯗鯙鯠鯤鯥鯫鯰鯷鯸鯿鰂鰆鶼鰉鰋鰐鰒鰕鰛鰜鰣鰤鰥鰦鰨鰩鰮鰳鰶鰷鱺鰼鰽鱀鱄鱅鱆鱈鱎鱐鱓鱔鱖鱘鱟鱠鱣鱨鱭鱮鱲鱵鱻鲅鳦鳧鳯鳲鳷鳻鴂鴃鴄鴆鴈鴎鴒鴔鴗鴛鴦鴝鵒鴟鴠鴢鴣鴥鴯鶓鴳鴴鴷鴽鵀鵁鵂鵓鵖鵙鵜鶘鵞鵟鵩鵪鵫鵵鵷鵻鵾鶂鶊鶏鶒鶖鶗鶡鶤鶦鶬鶱鶲鶵鶸鶹鶺鶿鷀鷁鷃鷄鷇鷈鷉鷊鷏鷓鷕鷖鷙鷞鷟鷥鷦鷯鷩鷫鷭鷳鷴鷽鷾鷿鸂鸇鸊鸏鸑鸒鸓鸕鸛鸜鸝鹸鹹鹺麀麂麃麄麇麋麌麐麑麒麚麛麝麤麩麪麫麮麯麰麺麾黁黈黌黢黒黓黕黙黝黟黥黦黧黮黰黱黲黶黹黻黼黽黿鼂鼃鼅鼈鼉鼏鼐鼒鼕鼖鼙鼚鼛鼡鼩鼱鼪鼫鼯鼷鼽齁齆齇齈齉齌齎齏齔齕齗齙齚齜齞齟齬齠齢齣齧齩齮齯齰齱齵齾龎龑龒龔龖龘龝龡龢龤" + +assert len(simplified_charcters) == len(simplified_charcters) + +s2t_dict = {} +t2s_dict = {} +for i, item in enumerate(simplified_charcters): + s2t_dict[item] = traditional_characters[i] + t2s_dict[traditional_characters[i]] = item + + +def tranditional_to_simplified(text: str) -> str: + return "".join([t2s_dict[item] if item in t2s_dict else item for item in text]) + + +ANCHOR_CHAR = "▁" + + +def prepare_onnx_input( + tokenizer, + labels: List[str], + char2phonemes: Dict[str, List[int]], + chars: List[str], + texts: List[str], + query_ids: List[int], + use_mask: bool = False, + window_size: int = None, + max_len: int = 512, +) -> Dict[str, np.array]: + if window_size is not None: + truncated_texts, truncated_query_ids = _truncate_texts( + window_size=window_size, texts=texts, query_ids=query_ids + ) + input_ids = [] + token_type_ids = [] + attention_masks = [] + phoneme_masks = [] + char_ids = [] + position_ids = [] + + for idx in range(len(texts)): + text = (truncated_texts if window_size else texts)[idx].lower() + query_id = (truncated_query_ids if window_size else query_ids)[idx] + + try: + tokens, text2token, token2text = tokenize_and_map( + tokenizer=tokenizer, text=text + ) + except Exception: + print(f'warning: text "{text}" is invalid') + return {} + + text, query_id, tokens, text2token, token2text = _truncate( + max_len=max_len, + text=text, + query_id=query_id, + tokens=tokens, + text2token=text2token, + token2text=token2text, + ) + + processed_tokens = ["[CLS]"] + tokens + ["[SEP]"] + + input_id = list(np.array(tokenizer.convert_tokens_to_ids(processed_tokens))) + token_type_id = list(np.zeros((len(processed_tokens),), dtype=int)) + attention_mask = list(np.ones((len(processed_tokens),), dtype=int)) + + query_char = text[query_id] + phoneme_mask = ( + [1 if i in char2phonemes[query_char] else 0 for i in range(len(labels))] + if use_mask + else [1] * len(labels) + ) + char_id = chars.index(query_char) + position_id = text2token[query_id] + 1 # [CLS] token locate at first place + + input_ids.append(input_id) + token_type_ids.append(token_type_id) + attention_masks.append(attention_mask) + phoneme_masks.append(phoneme_mask) + char_ids.append(char_id) + position_ids.append(position_id) + + outputs = { + "input_ids": np.array(input_ids).astype(np.int64), + "token_type_ids": np.array(token_type_ids).astype(np.int64), + "attention_masks": np.array(attention_masks).astype(np.int64), + "phoneme_masks": np.array(phoneme_masks).astype(np.float32), + "char_ids": np.array(char_ids).astype(np.int64), + "position_ids": np.array(position_ids).astype(np.int64), + } + return outputs + + +def _truncate_texts( + window_size: int, texts: List[str], query_ids: List[int] +) -> Tuple[List[str], List[int]]: + truncated_texts = [] + truncated_query_ids = [] + for text, query_id in zip(texts, query_ids): + start = max(0, query_id - window_size // 2) + end = min(len(text), query_id + window_size // 2) + truncated_text = text[start:end] + truncated_texts.append(truncated_text) + + truncated_query_id = query_id - start + truncated_query_ids.append(truncated_query_id) + return truncated_texts, truncated_query_ids + +def rule(C, V, R, T): + """Generate a syllable given the initial, the final, erhua indicator, and tone. + Orthographical rules for pinyin are applied. (special case for y, w, ui, un, iu) + + Note that in this system, 'ü' is alway written as 'v' when appeared in phoneme, but converted to + 'u' in syllables when certain conditions are satisfied. + + 'i' is distinguished when appeared in phonemes, and separated into 3 categories, 'i', 'ii' and 'iii'. + Erhua is possibly applied to every finals, except for finals that already ends with 'r'. + When a syllable is impossible or does not have any characters with this pronunciation, return None + to filter it out. + """ + + # 不可拼的音节, ii 只能和 z, c, s 拼 + if V in ["ii"] and (C not in ['z', 'c', 's']): + return None + # iii 只能和 zh, ch, sh, r 拼 + if V in ['iii'] and (C not in ['zh', 'ch', 'sh', 'r']): + return None + + # 齐齿呼或者撮口呼不能和 f, g, k, h, zh, ch, sh, r, z, c, s + if (V not in ['ii', 'iii']) and V[0] in ['i', 'v'] and ( + C in ['f', 'g', 'k', 'h', 'zh', 'ch', 'sh', 'r', 'z', 'c', 's']): + return None + + # 撮口呼只能和 j, q, x l, n 拼 + if V.startswith("v"): + # v, ve 只能和 j ,q , x, n, l 拼 + if V in ['v', 've']: + if C not in ['j', 'q', 'x', 'n', 'l', '']: + return None + # 其他只能和 j, q, x 拼 + else: + if C not in ['j', 'q', 'x', '']: + return None + + # j, q, x 只能和齐齿呼或者撮口呼拼 + if (C in ['j', 'q', 'x']) and not ( + (V not in ['ii', 'iii']) and V[0] in ['i', 'v']): + return None + + # b, p ,m, f 不能和合口呼拼,除了 u 之外 + # bm p, m, f 不能和撮口呼拼 + if (C in ['b', 'p', 'm', 'f']) and ((V[0] in ['u', 'v'] and V != "u") or + V == 'ong'): + return None + + # ua, uai, uang 不能和 d, t, n, l, r, z, c, s 拼 + if V in ['ua', 'uai', + 'uang'] and C in ['d', 't', 'n', 'l', 'r', 'z', 'c', 's']: + return None + + # sh 和 ong 不能拼 + if V == 'ong' and C in ['sh']: + return None + + # o 和 gkh, zh ch sh r z c s 不能拼 + if V == "o" and C in [ + 'd', 't', 'n', 'g', 'k', 'h', 'zh', 'ch', 'sh', 'r', 'z', 'c', 's' + ]: + return None + + # ueng 只是 weng 这个 ad-hoc 其他情况下都是 ong + if V == 'ueng' and C != '': + return + + # 非儿化的 er 只能单独存在 + if V == 'er' and C != '': + return None + + if C == '': + if V in ["i", "in", "ing"]: + C = 'y' + elif V == 'u': + C = 'w' + elif V.startswith('i') and V not in ["ii", "iii"]: + C = 'y' + V = V[1:] + elif V.startswith('u'): + C = 'w' + V = V[1:] + elif V.startswith('v'): + C = 'yu' + V = V[1:] + else: + if C in ['j', 'q', 'x']: + if V.startswith('v'): + V = re.sub('v', 'u', V) + if V == 'iou': + V = 'iu' + elif V == 'uei': + V = 'ui' + elif V == 'uen': + V = 'un' + result = C + V + + # Filter er 不能再儿化 + if result.endswith('r') and R == 'r': + return None + + # ii and iii, change back to i + result = re.sub(r'i+', 'i', result) + + result = result + R + T + return result + + +def generate_lexicon(with_tone=False, with_erhua=False): + """Generate lexicon for Mandarin Chinese.""" + syllables = OrderedDict() + + for C in [''] + INITIALS: + for V in FINALS: + for R in [''] if not with_erhua else ['', 'r']: + for T in [''] if not with_tone else ['1', '2', '3', '4', '5']: + result = rule(C, V, R, T) + if result: + syllables[result] = f'{C} {V}{R}{T}' + return syllables + +def _truncate( + max_len: int, + text: str, + query_id: int, + tokens: List[str], + text2token: List[int], + token2text: List[Tuple[int]], +): + truncate_len = max_len - 2 + if len(tokens) <= truncate_len: + return (text, query_id, tokens, text2token, token2text) + + token_position = text2token[query_id] + + token_start = token_position - truncate_len // 2 + token_end = token_start + truncate_len + font_exceed_dist = -token_start + back_exceed_dist = token_end - len(tokens) + if font_exceed_dist > 0: + token_start += font_exceed_dist + token_end += font_exceed_dist + elif back_exceed_dist > 0: + token_start -= back_exceed_dist + token_end -= back_exceed_dist + + start = token2text[token_start][0] + end = token2text[token_end - 1][1] + + return ( + text[start:end], + query_id - start, + tokens[token_start:token_end], + [i - token_start if i is not None else None for i in text2token[start:end]], + [(s - start, e - start) for s, e in token2text[token_start:token_end]], + ) + + +def get_phoneme_labels( + polyphonic_chars: List[List[str]], +) -> Tuple[List[str], Dict[str, List[int]]]: + labels = sorted(list(set([phoneme for char, phoneme in polyphonic_chars]))) + char2phonemes = {} + for char, phoneme in polyphonic_chars: + if char not in char2phonemes: + char2phonemes[char] = [] + char2phonemes[char].append(labels.index(phoneme)) + return labels, char2phonemes + + +def get_char_phoneme_labels( + polyphonic_chars: List[List[str]], +) -> Tuple[List[str], Dict[str, List[int]]]: + labels = sorted( + list(set([f"{char} {phoneme}" for char, phoneme in polyphonic_chars])) + ) + char2phonemes = {} + for char, phoneme in polyphonic_chars: + if char not in char2phonemes: + char2phonemes[char] = [] + char2phonemes[char].append(labels.index(f"{char} {phoneme}")) + return labels, char2phonemes + + +def wordize_and_map(text: str): + words = [] + index_map_from_text_to_word = [] + index_map_from_word_to_text = [] + while len(text) > 0: + match_space = re.match(r"^ +", text) + if match_space: + space_str = match_space.group(0) + index_map_from_text_to_word += [None] * len(space_str) + text = text[len(space_str) :] + continue + + match_en = re.match(r"^[a-zA-Z0-9]+", text) + if match_en: + en_word = match_en.group(0) + + word_start_pos = len(index_map_from_text_to_word) + word_end_pos = word_start_pos + len(en_word) + index_map_from_word_to_text.append((word_start_pos, word_end_pos)) + + index_map_from_text_to_word += [len(words)] * len(en_word) + + words.append(en_word) + text = text[len(en_word) :] + else: + word_start_pos = len(index_map_from_text_to_word) + word_end_pos = word_start_pos + 1 + index_map_from_word_to_text.append((word_start_pos, word_end_pos)) + + index_map_from_text_to_word += [len(words)] + + words.append(text[0]) + text = text[1:] + return words, index_map_from_text_to_word, index_map_from_word_to_text + + +def tokenize_and_map(tokenizer, text: str): + words, text2word, word2text = wordize_and_map(text=text) + + tokens = [] + index_map_from_token_to_text = [] + for word, (word_start, word_end) in zip(words, word2text): + word_tokens = tokenizer.tokenize(word) + + if len(word_tokens) == 0 or word_tokens == ["[UNK]"]: + index_map_from_token_to_text.append((word_start, word_end)) + tokens.append("[UNK]") + else: + current_word_start = word_start + for word_token in word_tokens: + word_token_len = len(re.sub(r"^##", "", word_token)) + index_map_from_token_to_text.append( + (current_word_start, current_word_start + word_token_len) + ) + current_word_start = current_word_start + word_token_len + tokens.append(word_token) + + index_map_from_text_to_token = text2word + for i, (token_start, token_end) in enumerate(index_map_from_token_to_text): + for token_pos in range(token_start, token_end): + index_map_from_text_to_token[token_pos] = i + + return tokens, index_map_from_text_to_token, index_map_from_token_to_text + + +def _load_config(config_path: os.PathLike): + import importlib.util + + spec = importlib.util.spec_from_file_location("__init__", config_path) + config = importlib.util.module_from_spec(spec) + spec.loader.exec_module(config) + return config + + +default_config_dict = { + "manual_seed": 1313, + "model_source": "bert-base-chinese", + "window_size": 32, + "num_workers": 2, + "use_mask": True, + "use_char_phoneme": False, + "use_conditional": True, + "param_conditional": { + "affect_location": "softmax", + "bias": True, + "char-linear": True, + "pos-linear": False, + "char+pos-second": True, + "char+pos-second_lowrank": False, + "lowrank_size": 0, + "char+pos-second_fm": False, + "fm_size": 0, + "fix_mode": None, + "count_json": "train.count.json", + }, + "lr": 5e-5, + "val_interval": 200, + "num_iter": 10000, + "use_focal": False, + "param_focal": {"alpha": 0.0, "gamma": 0.7}, + "use_pos": True, + "param_pos ": { + "weight": 0.1, + "pos_joint_training": True, + "train_pos_path": "train.pos", + "valid_pos_path": "dev.pos", + "test_pos_path": "test.pos", + }, +} + + +def load_config(config_path: os.PathLike, use_default: bool = False): + config = _load_config(config_path) + if use_default: + for attr, val in default_config_dict.items(): + if not hasattr(config, attr): + setattr(config, attr, val) + elif isinstance(val, dict): + d = getattr(config, attr) + for dict_k, dict_v in val.items(): + if dict_k not in d: + d[dict_k] = dict_v + return config + + +def predict( + session, onnx_input: Dict[str, Any], labels: List[str] +) -> Tuple[List[str], List[float]]: + all_preds = [] + all_confidences = [] + probs = session.run( + [], + { + "input_ids": onnx_input["input_ids"], + "token_type_ids": onnx_input["token_type_ids"], + "attention_mask": onnx_input["attention_masks"], + "phoneme_mask": onnx_input["phoneme_masks"], + "char_ids": onnx_input["char_ids"], + "position_ids": onnx_input["position_ids"], + }, + )[0] + + preds = np.argmax(probs, axis=1).tolist() + max_probs = [] + for index, arr in zip(preds, probs.tolist()): + max_probs.append(arr[index]) + all_preds += [labels[pred] for pred in preds] + all_confidences += max_probs + + return all_preds, all_confidences + + +class G2PWOnnxConverter: + def __init__( + self, + model_dir: str, + style: str = "bopomofo", + model_source: str = None, + enable_non_tradional_chinese: bool = False, + ): + + sess_options = onnxruntime.SessionOptions() + sess_options.graph_optimization_level = ( + onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL + ) + sess_options.execution_mode = onnxruntime.ExecutionMode.ORT_SEQUENTIAL + sess_options.intra_op_num_threads = 2 + self.punc = "、:,;。?!“”‘’':,;.?!" + self.rhy_phns = ['sp1', 'sp2', 'sp3', 'sp4'] + self.session_g2pW = onnxruntime.InferenceSession( + os.path.join(model_dir, "g2pW.onnx"), sess_options=sess_options + ) + self.config = load_config( + config_path=os.path.join(model_dir, "config.py"), use_default=True + ) + + self.model_source = model_source if model_source else self.config.model_source + self.enable_opencc = enable_non_tradional_chinese + self.tokenizer = BertTokenizer.from_pretrained(self.config.model_source) + self.vocab_phones = {} + self.vocab_tones = {} + with open(os.path.join(model_dir, "phone_id_map.txt"), 'rt', encoding='utf-8') as f: + phn_id = [line.strip().split() for line in f.readlines()] + for phn, id in phn_id: + self.vocab_phones[phn] = int(id) + self.pinyin2phone = generate_lexicon( + with_tone=True, with_erhua=False) + polyphonic_chars_path = os.path.join(model_dir, "POLYPHONIC_CHARS.txt") + monophonic_chars_path = os.path.join(model_dir, "MONOPHONIC_CHARS.txt") + self.polyphonic_chars = [ + line.split("\t") + for line in open(polyphonic_chars_path, encoding="utf-8") + .read() + .strip() + .split("\n") + ] + self.non_polyphonic = { + "一", + "不", + "和", + "咋", + "嗲", + "剖", + "差", + "攢", + "倒", + "難", + "奔", + "勁", + "拗", + "肖", + "瘙", + "誒", + "泊", + "听", + "噢", + } + self.non_monophonic = {"似", "攢"} + self.monophonic_chars = [ + line.split("\t") + for line in open(monophonic_chars_path, encoding="utf-8") + .read() + .strip() + .split("\n") + ] + self.labels, self.char2phonemes = ( + get_char_phoneme_labels(polyphonic_chars=self.polyphonic_chars) + if self.config.use_char_phoneme + else get_phoneme_labels(polyphonic_chars=self.polyphonic_chars) + ) + + self.chars = sorted(list(self.char2phonemes.keys())) + + self.polyphonic_chars_new = set(self.chars) + for char in self.non_polyphonic: + if char in self.polyphonic_chars_new: + self.polyphonic_chars_new.remove(char) + + self.monophonic_chars_dict = { + char: phoneme for char, phoneme in self.monophonic_chars + } + for char in self.non_monophonic: + if char in self.monophonic_chars_dict: + self.monophonic_chars_dict.pop(char) + + self.pos_tags = ["UNK", "A", "C", "D", "I", "N", "P", "T", "V", "DE", "SHI"] + + with open( + os.path.join(model_dir, "bopomofo_to_pinyin_wo_tune_dict.json"), + "r", + encoding="utf-8", + ) as fr: + self.bopomofo_convert_dict = json.load(fr) + self.style_convert_func = { + "bopomofo": lambda x: x, + "pinyin": self._convert_bopomofo_to_pinyin, + }[style] + + with open( + os.path.join(model_dir, "char_bopomofo_dict.json"), "r", encoding="utf-8" + ) as fr: + self.char_bopomofo_dict = json.load(fr) + + if self.enable_opencc: + self.cc = OpenCC("s2tw") + def _pinyin2p(self,pinyins: List[str],words: List[str])->List[str]: + sub_initials = [] + sub_finals = [] + phones_list = [] + for seg in words: + seg = re.sub('[a-zA-Z]+', '', seg) + # [(word, pos), ...] + seg_cut = psg.lcut(seg) + # fix wordseg bad case for sandhi + # seg_cut = self.tone_modifier.pre_merge_for_modify(seg_cut) + # 为了多音词获得更好的效果,这里采用整句预测 + phones = [] + initials = [] + finals = [] + pre_word_length = 0 + for word, pos in seg_cut: + sub_initials = [] + sub_finals = [] + now_word_length = pre_word_length + len(word) + + # skip english word + if pos == 'eng': + pre_word_length = now_word_length + continue + + word_pinyins = pinyins[pre_word_length:now_word_length] + + # 多音字消歧 + # word_pinyins = self.corrector.correct_pronunciation( + # word, word_pinyins) + + for word_pinyin, char in zip(word_pinyins, word): + if word_pinyin is None: + word_pinyin = char + + word_pinyin = word_pinyin.replace("u:", "v") + + if word_pinyin in self.pinyin2phone: + initial_final_list = self.pinyin2phone[ + word_pinyin].split(" ") + if len(initial_final_list) == 2: + sub_initials.append(initial_final_list[0]) + sub_finals.append(initial_final_list[1]) + elif len(initial_final_list) == 1: + sub_initials.append('') + sub_finals.append(initial_final_list[1]) + else: + # If it's not pinyin (possibly punctuation) or no conversion is required + sub_initials.append(word_pinyin) + sub_finals.append(word_pinyin) + + pre_word_length = now_word_length + # # tone sandhi + # sub_finals = self.tone_modifier.modified_tone(word, pos, + # sub_finals) + # er hua + # if with_erhua: + # sub_initials, sub_finals = self._merge_erhua( + # sub_initials, sub_finals, word, pos) + + initials.append(sub_initials) + finals.append(sub_finals) + initials = sum(initials, []) + finals = sum(finals, []) + for c, v in zip(initials, finals): + # NOTE: post process for pypinyin outputs + # we discriminate i, ii and iii + if c and c not in self.punc: + phones.append(c) + # replace punctuation by `sp` + if c and c in self.punc: + phones.append('sp') + + if v and v not in self.punc and v not in self.rhy_phns: + phones.append(v) + phones_list.append(phones) + return phones_list + + + def _p2id(self, phonemes: List[str]) -> np.ndarray: + """ + Phoneme to Index + """ + # replace unk phone with sp + phonemes = [ + phn if phn in self.vocab_phones else "sp" for phn in phonemes + ] + phone_ids = [self.vocab_phones[item] for item in phonemes] + return np.array(phone_ids, np.int64) + + def _convert_bopomofo_to_pinyin(self, bopomofo: str) -> str: + tone = bopomofo[-1] + assert tone in "12345" + component = self.bopomofo_convert_dict.get(bopomofo[:-1]) + if component: + return component + tone + else: + print(f'Warning: "{bopomofo}" cannot convert to pinyin') + return None + + def __call__(self, sentences: List[str]) -> List[List[str]]: + if isinstance(sentences, str): + sentences = [sentences] + + if self.enable_opencc: + translated_sentences = [] + for sent in sentences: + translated_sent = self.cc.convert(sent) + assert len(translated_sent) == len(sent) + translated_sentences.append(translated_sent) + sentences = translated_sentences + + texts, query_ids, sent_ids, partial_results = self._prepare_data( + sentences=sentences + ) + if len(texts) == 0: + # sentences no polyphonic words + return partial_results + + onnx_input = prepare_onnx_input( + tokenizer=self.tokenizer, + labels=self.labels, + char2phonemes=self.char2phonemes, + chars=self.chars, + texts=texts, + query_ids=query_ids, + use_mask=self.config.use_mask, + window_size=None, + ) + + preds, confidences = predict( + session=self.session_g2pW, onnx_input=onnx_input, labels=self.labels + ) + if self.config.use_char_phoneme: + preds = [pred.split(" ")[1] for pred in preds] + + results = partial_results + for sent_id, query_id, pred in zip(sent_ids, query_ids, preds): + results[sent_id][query_id] = self.style_convert_func(pred) + phones = self._pinyin2p(results[0],texts) + phone_ids = self._p2id(phones[0]) + return {"phones": phones[0], "phone_ids": phone_ids, "pinyins": results[0]} + + def _prepare_data( + self, sentences: List[str] + ) -> Tuple[List[str], List[int], List[int], List[List[str]]]: + texts, query_ids, sent_ids, partial_results = [], [], [], [] + for sent_id, sent in enumerate(sentences): + # pypinyin works well for Simplified Chinese than Traditional Chinese + sent_s = tranditional_to_simplified(sent) + pypinyin_result = pinyin( + sent_s, neutral_tone_with_five=True, style=Style.TONE3 + ) + partial_result = [None] * len(sent) + for i, char in enumerate(sent): + if char in self.polyphonic_chars_new: + texts.append(sent) + query_ids.append(i) + sent_ids.append(sent_id) + elif char in self.monophonic_chars_dict: + partial_result[i] = self.style_convert_func( + self.monophonic_chars_dict[char] + ) + elif char in self.char_bopomofo_dict: + partial_result[i] = pypinyin_result[i][0] + # partial_result[i] = self.style_convert_func(self.char_bopomofo_dict[char][0]) + else: + partial_result[i] = pypinyin_result[i][0] + + partial_results.append(partial_result) + return texts, query_ids, sent_ids, partial_results diff --git a/paddlex/inference/models/text_to_pinyin/result.py b/paddlex/inference/models/text_to_pinyin/result.py new file mode 100644 index 0000000000..77338d79b0 --- /dev/null +++ b/paddlex/inference/models/text_to_pinyin/result.py @@ -0,0 +1,21 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ...common.result import BaseResult +import copy + +class TextToPinyinResult(BaseResult): + + def __init__(self, data: dict) -> None: + super().__init__(data) diff --git a/paddlex/inference/models/text_to_speech_acoustic/__init__.py b/paddlex/inference/models/text_to_speech_acoustic/__init__.py new file mode 100644 index 0000000000..2cd9f28023 --- /dev/null +++ b/paddlex/inference/models/text_to_speech_acoustic/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .predictor import Fastspeech2Predictor diff --git a/paddlex/inference/models/text_to_speech_acoustic/predictor.py b/paddlex/inference/models/text_to_speech_acoustic/predictor.py new file mode 100644 index 0000000000..d2a159c5e8 --- /dev/null +++ b/paddlex/inference/models/text_to_speech_acoustic/predictor.py @@ -0,0 +1,69 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np + +from ...common.batch_sampler import AudioBatchSampler + +from ..base import BasePredictor +from .result import Fastspeech2Result +from ....modules.text_to_speech_acoustic.model_list import MODELS + + + +class Fastspeech2Predictor(BasePredictor): + + entities = MODELS + + def __init__(self, *args, **kwargs): + """Initializes FastspeechPredictor. + + Args: + *args: Arbitrary positional arguments passed to the superclass. + **kwargs: Arbitrary keyword arguments passed to the superclass. + """ + super().__init__(*args, **kwargs) + self.infer = self.create_static_infer() + def _build_batch_sampler(self): + """Builds and returns an AudioBatchSampler instance. + + Returns: + AudioBatchSampler: An instance of AudioBatchSampler. + """ + return AudioBatchSampler() + + def _get_result_class(self): + """Returns the result class, Fastspeech2Result. + + Returns: + type: The Fastspeech2Result class. + """ + return Fastspeech2Result + + def process(self, batch_data): + """ + Process a batch of data through the preprocessing, inference, and postprocessing. + + Args: + batch_data (List[Union[str], ...]): A batch of input phone data. + + Returns: + dict: A dictionary containing the input path and result. The result include the output pinyin dict. + """ + phone = batch_data + mel = self.infer(phone) + return { + "result": mel, + } + diff --git a/paddlex/inference/models/text_to_speech_acoustic/processors.py b/paddlex/inference/models/text_to_speech_acoustic/processors.py new file mode 100644 index 0000000000..686923cae1 --- /dev/null +++ b/paddlex/inference/models/text_to_speech_acoustic/processors.py @@ -0,0 +1,14 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/paddlex/inference/models/text_to_speech_acoustic/result.py b/paddlex/inference/models/text_to_speech_acoustic/result.py new file mode 100644 index 0000000000..8878e5bb5c --- /dev/null +++ b/paddlex/inference/models/text_to_speech_acoustic/result.py @@ -0,0 +1,21 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from ...common.result import BaseResult + +class Fastspeech2Result(BaseResult): + + def __init__(self, data: dict) -> None: + super().__init__(data) \ No newline at end of file diff --git a/paddlex/inference/models/text_to_speech_vocoder/__init__.py b/paddlex/inference/models/text_to_speech_vocoder/__init__.py new file mode 100644 index 0000000000..cb66bfc0e1 --- /dev/null +++ b/paddlex/inference/models/text_to_speech_vocoder/__init__.py @@ -0,0 +1,15 @@ +# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .predictor import PwganPredictor diff --git a/paddlex/inference/models/text_to_speech_vocoder/predictor.py b/paddlex/inference/models/text_to_speech_vocoder/predictor.py new file mode 100644 index 0000000000..b55f1e2d0c --- /dev/null +++ b/paddlex/inference/models/text_to_speech_vocoder/predictor.py @@ -0,0 +1,70 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np + +from ....utils.func_register import FuncRegister +from ...common.batch_sampler import AudioBatchSampler + +from ..base import BasePredictor +from .result import PwganResult +from ....modules.text_to_speech_vocoder.model_list import MODELS + + +class PwganPredictor(BasePredictor): + + entities = MODELS + + def __init__(self, *args, **kwargs): + """Initializes FastspeechPredictor. + + Args: + *args: Arbitrary positional arguments passed to the superclass. + **kwargs: Arbitrary keyword arguments passed to the superclass. + """ + super().__init__(*args, **kwargs) + self.infer = self.create_static_infer() + def _build_batch_sampler(self): + """Builds and returns an AudioBatchSampler instance. + + Returns: + AudioBatchSampler: An instance of AudioBatchSampler. + """ + return AudioBatchSampler() + + def _get_result_class(self): + """Returns the result class, PwganResult. + + Returns: + type: The PwganResult class. + """ + return PwganResult + + def process(self, batch_data): + """ + Process a batch of data through the preprocessing, inference, and postprocessing. + + Args: + batch_data (List[Union[str], ...]): A batch of input phone data. + + Returns: + dict: A dictionary containing the input path and result. The result include the output pinyin dict. + """ + mel = batch_data[0] + wav = self.infer([mel]) + result = np.array(wav).reshape(1,-1) + return { + "result": result, + } + diff --git a/paddlex/inference/models/text_to_speech_vocoder/processors.py b/paddlex/inference/models/text_to_speech_vocoder/processors.py new file mode 100644 index 0000000000..686923cae1 --- /dev/null +++ b/paddlex/inference/models/text_to_speech_vocoder/processors.py @@ -0,0 +1,14 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + diff --git a/paddlex/inference/models/text_to_speech_vocoder/result.py b/paddlex/inference/models/text_to_speech_vocoder/result.py new file mode 100644 index 0000000000..5f4c7ab64c --- /dev/null +++ b/paddlex/inference/models/text_to_speech_vocoder/result.py @@ -0,0 +1,23 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from ...common.result import BaseAudioResult + + +class PwganResult(BaseAudioResult): + + def __init__(self, data: dict) -> None: + super().__init__(data) + def _to_audio(self): + return {"res": self} \ No newline at end of file diff --git a/paddlex/inference/utils/io/__init__.py b/paddlex/inference/utils/io/__init__.py index 8b66633b00..5f9131c966 100644 --- a/paddlex/inference/utils/io/__init__.py +++ b/paddlex/inference/utils/io/__init__.py @@ -34,4 +34,5 @@ WriterType, XlsxWriter, YAMLWriter, + AudioWriter, ) diff --git a/paddlex/inference/utils/io/writers.py b/paddlex/inference/utils/io/writers.py index 865e87ad56..71c650ef65 100644 --- a/paddlex/inference/utils/io/writers.py +++ b/paddlex/inference/utils/io/writers.py @@ -19,6 +19,7 @@ import numpy as np import pandas as pd +import soundfile as sf import yaml from PIL import Image @@ -56,7 +57,7 @@ class WriterType(enum.Enum): YAML = 8 MARKDOWN = 9 TXT = 10 - + AUDIO = 11 class _BaseWriter(object): """_BaseWriter""" @@ -260,6 +261,26 @@ def get_type(self): """get type""" return WriterType.MARKDOWN +class AudioWriter(_BaseWriter): + """AudioWriter""" + + def __init__(self, sample_rate=24000, backend="wav", **bk_args): + super().__init__(sample_rate=sample_rate, backend=backend, **bk_args) + self.sample_rate = sample_rate + def write(self, out_path, obj): + """write""" + return self._backend.write_obj(str(out_path), obj) + + def _init_backend(self, bk_type, bk_args): + """init backend""" + if bk_type == "wav": + return AudioWriterBackend(**bk_args) + else: + raise ValueError("Unsupported backend type") + + def get_type(self): + """get type""" + return WriterType.AUDIO class _BaseWriterBackend(object): """_BaseWriterBackend""" @@ -458,3 +479,15 @@ def _write_obj(self, out_path, obj): """write markdown obj""" with open(out_path, mode="w", encoding="utf-8", errors="replace") as f: f.write(obj) + + +class AudioWriterBackend(_BaseWriterBackend): + """AudioWriterBackend""" + + def __init__(self,sample_rate=24000): + super().__init__() + self.sample_rate = sample_rate + def _write_obj(self, out_path, obj): + """write audio obj""" + audio = obj['result'] + sf.write(out_path, audio, self.sample_rate) \ No newline at end of file diff --git a/paddlex/inference/utils/official_models.py b/paddlex/inference/utils/official_models.py index a5d46429d4..e111b9fe77 100644 --- a/paddlex/inference/utils/official_models.py +++ b/paddlex/inference/utils/official_models.py @@ -364,6 +364,9 @@ "PP-DocBee2-3B": "https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0.0/PP-DocBee2-3B_infer.tar", "latin_PP-OCRv5_mobile_rec": "https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0.0/latin_PP-OCRv5_mobile_rec_infer.tar", "korean_PP-OCRv5_mobile_rec": "https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0.0/korean_PP-OCRv5_mobile_rec_infer.tar", + "G2PWModel": "https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0rc0/G2PWModel_1.1.tar", + "fastspeech2_csmsc": "https://paddlespeech.bj.bcebos.com/tmp/fastspeech2_csmsc.tar", + "pwgan_csmsc": "https://paddlespeech.bj.bcebos.com/tmp/pwgan_csmsc.tar", } diff --git a/paddlex/modules/__init__.py b/paddlex/modules/__init__.py index ca3370d812..5e00c202c3 100644 --- a/paddlex/modules/__init__.py +++ b/paddlex/modules/__init__.py @@ -88,6 +88,14 @@ TextRecExportor, TextRecTrainer, ) +from .text_to_speech_vocoder import TextToSpeechVocoderDatasetChecker +from .text_to_speech_acoustic import TextToSpeechAcousticDatasetChecker +from .text_to_pinyin import ( + TextToPinyinDatasetChecker, + TextToPinyinEvaluator, + TextToPinyinExportor, + TextToPinyinTrainer, +) from .ts_anomaly_detection import ( TSADDatasetChecker, TSADEvaluator, diff --git a/paddlex/modules/text_to_pinyin/__init__.py b/paddlex/modules/text_to_pinyin/__init__.py new file mode 100644 index 0000000000..2a2987ff64 --- /dev/null +++ b/paddlex/modules/text_to_pinyin/__init__.py @@ -0,0 +1,18 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .dataset_checker import TextToPinyinDatasetChecker +from .trainer import TextToPinyinTrainer +from .evaluator import TextToPinyinEvaluator +from .exportor import TextToPinyinExportor \ No newline at end of file diff --git a/paddlex/modules/text_to_pinyin/dataset_checker.py b/paddlex/modules/text_to_pinyin/dataset_checker.py new file mode 100644 index 0000000000..12d222ac89 --- /dev/null +++ b/paddlex/modules/text_to_pinyin/dataset_checker.py @@ -0,0 +1,27 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .model_list import MODELS +from ..base import BaseDatasetChecker +from ...utils.errors import UnsupportedAPIError + + +class TextToPinyinDatasetChecker(BaseDatasetChecker): + """Dataset Checker for G2PWModel Model""" + + entities = MODELS + + def __init__(self, config): + # not support for now + raise UnsupportedAPIError("G2PWModel model not support data check for now.") \ No newline at end of file diff --git a/paddlex/modules/text_to_pinyin/evaluator.py b/paddlex/modules/text_to_pinyin/evaluator.py new file mode 100644 index 0000000000..9731bc5a9b --- /dev/null +++ b/paddlex/modules/text_to_pinyin/evaluator.py @@ -0,0 +1,27 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .model_list import MODELS +from ..base import BaseEvaluator +from ...utils.errors import UnsupportedAPIError + + +class TextToPinyinEvaluator(BaseEvaluator): + """Instance G2PWModel Model Evaluator""" + + entities = MODELS + + def __init__(self, config): + # not support for now + raise UnsupportedAPIError("G2PWModel model not support evaluate for now.") \ No newline at end of file diff --git a/paddlex/modules/text_to_pinyin/exportor.py b/paddlex/modules/text_to_pinyin/exportor.py new file mode 100644 index 0000000000..3b0b45ade7 --- /dev/null +++ b/paddlex/modules/text_to_pinyin/exportor.py @@ -0,0 +1,27 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .model_list import MODELS +from ..base import BaseExportor +from ...utils.errors import UnsupportedAPIError + + +class TextToPinyinExportor(BaseExportor): + """Instance G2PWModel Model Exportor""" + + entities = MODELS + + def __init__(self, config): + # not support for now + raise UnsupportedAPIError("G2PWModel model not support export for now.") \ No newline at end of file diff --git a/paddlex/modules/text_to_pinyin/model_list.py b/paddlex/modules/text_to_pinyin/model_list.py new file mode 100644 index 0000000000..1881466820 --- /dev/null +++ b/paddlex/modules/text_to_pinyin/model_list.py @@ -0,0 +1,18 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +MODELS = [ + "G2PWModel", +] \ No newline at end of file diff --git a/paddlex/modules/text_to_pinyin/trainer.py b/paddlex/modules/text_to_pinyin/trainer.py new file mode 100644 index 0000000000..b72dcf4ffc --- /dev/null +++ b/paddlex/modules/text_to_pinyin/trainer.py @@ -0,0 +1,40 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .model_list import MODELS +from ..base import BaseTrainer +from ...utils.errors import UnsupportedAPIError + + +class TextToPinyinTrainer(BaseTrainer): + """Instance G2PWModel Model Trainer""" + + entities = MODELS + + def __init__(self, config): + # not support for now + raise UnsupportedAPIError("G2PWModel model not support train for now.") + + def update_config(self): + """update training config""" + pass + + def get_train_kwargs(self) -> dict: + """get key-value arguments of model training function + + Returns: + dict: the arguments of training function. + """ + train_args = {"device": self.get_device()} + return train_args \ No newline at end of file diff --git a/paddlex/modules/text_to_speech_acoustic/__init__.py b/paddlex/modules/text_to_speech_acoustic/__init__.py new file mode 100644 index 0000000000..d5b01de2e7 --- /dev/null +++ b/paddlex/modules/text_to_speech_acoustic/__init__.py @@ -0,0 +1,18 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .dataset_checker import TextToSpeechAcousticDatasetChecker +# from .trainer import TextToSpeechTrainer +# from .evaluator import TextToSpeechEvaluator +# from .exportor import TextToSpeechExportor diff --git a/paddlex/modules/text_to_speech_acoustic/dataset_checker.py b/paddlex/modules/text_to_speech_acoustic/dataset_checker.py new file mode 100644 index 0000000000..83521e3ae8 --- /dev/null +++ b/paddlex/modules/text_to_speech_acoustic/dataset_checker.py @@ -0,0 +1,27 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .model_list import MODELS +from ..base import BaseDatasetChecker +from ...utils.errors import UnsupportedAPIError + + +class TextToSpeechAcousticDatasetChecker(BaseDatasetChecker): + """Dataset Checker for Fastspeech2 Model""" + + entities = MODELS + + def __init__(self, config): + # not support for now + raise UnsupportedAPIError("Fastspeech2Model model not support data check for now.") diff --git a/paddlex/modules/text_to_speech_acoustic/model_list.py b/paddlex/modules/text_to_speech_acoustic/model_list.py new file mode 100644 index 0000000000..e43f684ce0 --- /dev/null +++ b/paddlex/modules/text_to_speech_acoustic/model_list.py @@ -0,0 +1,17 @@ +# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +MODELS = [ + "fastspeech2_csmsc", +] \ No newline at end of file diff --git a/paddlex/modules/text_to_speech_vocoder/__init__.py b/paddlex/modules/text_to_speech_vocoder/__init__.py new file mode 100644 index 0000000000..8575483b15 --- /dev/null +++ b/paddlex/modules/text_to_speech_vocoder/__init__.py @@ -0,0 +1,18 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .dataset_checker import TextToSpeechVocoderDatasetChecker +# from .trainer import TextToSpeechTrainer +# from .evaluator import TextToSpeechEvaluator +# from .exportor import TextToSpeechExportor diff --git a/paddlex/modules/text_to_speech_vocoder/dataset_checker.py b/paddlex/modules/text_to_speech_vocoder/dataset_checker.py new file mode 100644 index 0000000000..e2d7fd3b62 --- /dev/null +++ b/paddlex/modules/text_to_speech_vocoder/dataset_checker.py @@ -0,0 +1,27 @@ +# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .model_list import MODELS +from ..base import BaseDatasetChecker +from ...utils.errors import UnsupportedAPIError + + +class TextToSpeechVocoderDatasetChecker(BaseDatasetChecker): + """Dataset Checker for Fastspeech2 Model""" + + entities = MODELS + + def __init__(self, config): + # not support for now + raise UnsupportedAPIError("Fastspeech2Model model not support data check for now.") diff --git a/paddlex/modules/text_to_speech_vocoder/model_list.py b/paddlex/modules/text_to_speech_vocoder/model_list.py new file mode 100644 index 0000000000..6461132ead --- /dev/null +++ b/paddlex/modules/text_to_speech_vocoder/model_list.py @@ -0,0 +1,17 @@ +# Copyright (c) 2025 PaddlePaddle Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +MODELS = [ + "pwgan_csmsc", +] \ No newline at end of file diff --git a/test.wav b/test.wav new file mode 100644 index 0000000000000000000000000000000000000000..207e5e9729db4b19f62b5d4d97fc1ba08589a846 GIT binary patch literal 73844 zcmZs?1$f*#6D}+ncfDRp(j;YOW3zFz-fV{%yl0FWLxx&bibG5`#)fWE!P^lC`+ z{3PG#@q;9g-z1TqN%Tl0$;3wN^sxQ;cM>OY6W9L^H@%M>F5;nwj~@R29RU)eN0=T8 zQR$)l`PT^gb7&;}4iiiWJtL`vFqF2)kKU5>YveyYMmoMoNj4``^;p|2#$NP5-4+^IuvssQkav zBCYzre~~sJ%9jXdGPv{alSX+Md5Z8d@}K@3;cFx%ox_o*2)`pog!38q=_wg`(|h8- zU!~t?)QSGrCy{=MoavPQm)?x@jCUFB_;1d1Ze;K!@*m+{#`#}Prl+OnNWZ3!(2SJG z)4zS1o|^uBdY>@=>zj-`k<^Sg|4oae{=e@so+EGm?a%c3(y7Rx`CrQZZ|X9rgmlzK z@DTn9^wJ~w(!b2em0kn$f4)wCPM6^fN#_2KZ1VK`bUEe!NOXqGrps;Qntm6lIlZn( zy%8$Y>y2E~QzG?8(lgqS{-5zSy6k721wTy>AE33C6Y5;V?=aAhE7S>I_V?p zzoRFehV(i!sLL2X|I(5{X}VTQzh>wtjZ&O(pFw3ht&y)XDF2uG$X$e*NZxd={$+#%*da9pI%OS z?dfmQOUr13`iK5V$?0uLZ#AOzN78~cHT=J}rMEQW8v0XHgq}#g$k*tPzKD#e2=6mA zNMu|^evuKDF%thI{Fx^s@}1FBk@uL6)ySyMkfC&$%#h27tO!6y4-x3;895^J9`~of z2nFcREbjZmJ?)R~h?JBuwj(rP5|O}<m<#r*$&)3jWlJX$vCbCGvwH!U_0?$_Vul zd5qK@(GHRF&>yLaTvhs&o~G;n(8d0ti3KKL{BxRt1gt;?QNTd|qp9^02eN=1ASXQw zg6tp@O)=0if<&fYkUw1Y5sB7iq$TNSO+~>|@;7J<&XKy{E~P#PNFiy&3c|!pzs&;v zqPeotyhUhU6K#u+=E+8>DFtFcE|8nPiKpdT>2n!S2s8khX|3_VMoNPUG*@BZqP69r zZIZ!QP#t_BIcXU|YC9Q#opb{y$yo3T%qDqh*=Eoi6a&9W4O(h6rP@yVgOWfYtv~@# zo_-BzX?a04${R2JN(WL&4@%PmT6agv!LOt?@R0JLBKScXff@8|H04Da<#lIJi!25$ zVM*GeTa<#Sq%zI37IXl&$t=(moS^y3fkd(!Q~`d{no^ren$Q+3r>(z4PE$&rlcDsz zNO|EQH7QMTlmne;u414W7(%9lfuJktN6SkjlPQ00({_ca4fr?ZUo;p3-jOVz5v3#( zXaiy?uX@wg{3LZ~%ifS3l#(vsJSahqgSlWHDFn8YsWjJqa)`DQ(B3&lh5;vS!)sCw zyru(aJEd4p_JGl#BDn%?gNBsOwx9xOL+fY)$!pqcjYu`v03e)9+t;6L21~&UDk<-1ohLzd@|76iHp(*>?bWB$ zwmnbju1l)G8>9oJIEKW)&A^Q>)B9btO^3*Ja1MOOmBBUW!gGj;DMdz+`>-CVO7_4y z;1VthM}U!-fzhxuSw=d-a^x_X0|$}n;0o~J7a%X}g%g1ezQQfRP%xYv21m(XpglB_ z-Q+jyNotdcuo6vq2d+_TycakF_K_0g5F7`3;*Y=rCsRwk0q6&o0ToZCB~&CUfeFTw z5u^gMAK%3n;d08~(_|qX33Dj_pMw2hDV__uQyTSTDcA?M;bq_?G$Rkl%x=~;gWPNg z*>Gh>pw#~W2Y^Jm*ar5ewdW&E;UwTkJ76l z27jUiFbFP03&yM$N~b=Y}d3 z&^&Psco$B^A9csj0{?3L7Peu~4;yk$R9RDmwfK1m>we;C;ezZ!E)IW3gW+4;jado; zikE+budCy^8RQ!H3zg*7Y6!g%yy_*W;am~<1^bb13g zF2DqRp1{~pFY8b|(=(dCt1l9$six^i;`z!p-i;I0g+gmmQR~dS0M}6ku#Ivc^Dop%wzV3FMxaeB<+^g zQe2JtDd*Wf{DHs}(pK7`Tt@IPn-`#o4lSCEhMoEL_REgiFU{pE6wy<#D#Lw5d7Xg(9e zoYB?@8KdZKWJh(AbJ zlX-^yi1skk8*gu4E@?fTwmoaxP730Gq}=@xreqa=e>tF8Ijl`xuYsmGC`a#&wO?fi3L^ zn-m3nd3+V+6Z!$(wdgxf&}k$yW7(E)0=^rpEoEkxxp(lVbV*unr529Xd6{;;i9${O zmahnDBh`Qd6`sAT+@vF}xz-1l7k05@gX7_Hwl7)_Q`k+xzsNN!tEXXQW+!e8i}Qw1ih7zw z%yjK2naeexCi(=j1(ecm@pagX+7dO5%x47h6Z7m_&_fFl4E})^u%3+K?~ryOJ!Zv! z*!*fo<}^5|&>0@a!m^B?JO+)}0E!OH5-xM^LWOV{t}c^;^5IA98SZ|#qSl`@=M$LG zI7ph%5sriB)HeJmE(Tpev-DlD;yuP@6HB7u+C)+c%!1wEPi+}`D&{9_;218h8wsB% zkNFl%mGEKpx;TpIsayw%bQVe`qp^S{@|T(Zs+U}3j%mY4K&VE>tMTwGJ0D-x2-v{h zC#y&~CM#wT17E|J+8&aLYYboDm*K3WmDmckQ6%9I%ZKY~lXQFGhHxErG*e0QXd(6` zTavUzS@Ap$z~_pd&C1`yGqDMGVt+#`p3F4FeZeTW8|7hZv)@pH_Kw{ETA{HZ6Y~oG zg>PX6jUflwl4zPXN9;^`gfB3eA*VQTSAH+Z9)nRf#8O4GIbW|Lq*3Wjb z9?!xJ!DTX#+JA#F3rDaGlv!FB9wML6WipK0jWbc}KtQp$H?@dokkU*JrU%Hus$`Z{ z9i9OgXq1xua2>G00^o#JnES+1X@M(XPDn^|CYjbVldL1rTs$;`wWKvX065}>*I+Bu z802GLlWO>@`iOL8`l3a+7%Yt!B7|OOOA$w{f=;Rv{lFKsgH+S*AV5hD=LPprC;UYatiEpS{PFWY@4ZW+i+ELPSUIf@soH zd!_wQ%Z3hxE2?p!523Zezy1BagT4MxBjvJ>xN>R@#E8zPcxD~3V zS`nkgDJWPOwF6FY2+RR{*`MrBrn?X;wh(WMye^wAi@t<8lL?qI$$7+bhO@?8rfC+= zywO-enx?y_i#1f09~?p7GABK3i~qAQ-smx#|5Kc%^<< z4eBQKgz^=ok_W)f#IYh%T6oVt5f>Y#=u61wrB`}II%_Ox?P*cWs=*}pmCqahHkUWm zGX0b^X^Xy;KD!?2ck2rA@A%cMi6>kY<~mae-oVxH8FZZ-!o#%>3LpLw`W?IQ%` zH2WWTo46Z#vbe^(y19qi%i21+?uA==j(W$Y?N4-hH#*08KLpx^mIStY7rJ`5;zFrf z4xr(2+I2025#ZSQTx0&Zexr0wYH!S8NRXGADw;|e<4vQY`ozu26gJuQ@AYLY-J{l6 z6D=Q&H4GQ^Z~4vQaJ^Z&s$ayNV8;OgW@1l*Wuyyop)CjRNsbBv}?YNc;mxG z*j9Xg)B$b4Yr$8G=3tAHCe*r}396A6+H!TcrlN~#=Wy*%*5GshbnjkY zYk!x(UVjtkQ(NY=qPB0gKW*Sn5ZyQ4g(`&4Xj6XU3U-#pW=c7F~u% z7B$)&v(#L|@JY9U>C2uGGl>CxXFeXRzy-C6I4jwWYN{#WQtDbYLA?^*A58X_@c>Vn zcU0g^u$nJyub);iB}dxLv^^Qg&d(3?m4ML zed}-sI^txQ0#ksGdoOr(8;v8(Ib%|zms!tP=UA&-=0~&EGL{_icQUWdGAi1sU#IIT zRkz~k=%~G>>$-28%p8Wz`A<4tG_j4zQY}eqsO?0#wU$b2B~3Y_0W~Td3dp`bo||s7 zUkUsj7~pZ(Dy0=p38dCaS(I`$sj&Twdem!mFH9z%aoPj-P+vtn9NEJ0;fb~hX@kN| z*m|a3lAgKCl;E21mBdXlHWkk_J$ANbZq$&dG|Q>z{?@L>+c8r7%q%A|Karp6AL)AN zb68HA%SO$y`1voOfQG;*F;)C3Gz0I@0iBd00FpSm~oR^sV2dtyr`R&)72I_o9v_<~a__!kFO;)V#SokE-JtsQcx zt+>N-LpscUg;!aFu9~z_&TkzPJwJ9=%w~(*`qNU+Adm6X`DaPqvQR?eQP)6ctYiFv=awpH@i363K(u#;L0_3g2ab&AF> ziz+XdVREoMH(u-}?~?zvw2rwIb2q9=R2Rz!b6hmBj5e;fypFyeH$8KH(@t&<_nTj0 z)J5$u-jY1rV>}UkMfsV0x>G`XSVv0=)(kJfy>SbK)wAKN+Az(jELS%wJ3{w-L%iF4 zB||M0t1sw0n--ID(bh4ga?0>+Ci-J_mXJ zFH^v9nwh|y;)7xd!x%Y>F<;dDm~NK!*5Bq$W5i zl{S8nyoNhsW7v*#M#Di-aS#6udhlfBzBV7%z^S+!YN4Ib)=^F_LQkkIBL_SBxj=#N z!0<=^Sr>8kOq*k$nRY0Vq+CmB5|V>+T*;16KX#|Qbg%HZ6_)fw3(#lZI{S@)1e(aX z3_n;adycE2@2#I9Ka;nahsQiKhs`Ce!!6BXo|yBv@_rWl(DE-=)%0184jNJXu{eetk*G<(c9%gd5^p+FkTCXm<|u9wU#1 z_4-5HGT2Obqid{xWH>A>Gro$(rW@w>)()1wnf6&W8|E2W7*|FQ%~UQ*G;9|y3W;KG zeF^<>LxOZj*h{%bz!UZZKaO3B3*lwRk6(d}cp2`F)~bWFzqBs+J;{t`DFXwi{5gWD z!6u<6-qr5S?yG5;>?M<#q&%r@J#PaoUDG@tl0foH*Cs%8uud_)EW7SK8o}GnunnmYNz_??m~nm80$(7fHR%n=P}W21hS4I;1T6IbtS# zPyH|2o+-LVycKq3Kd?hsJ;%|N+b?QMd?kfJDy6oxrm7s;jT@44$gjAAp8_ROy-R=GadiR-O0BXmjqs?PR|@c$0c)d*J- z7H5`modiaIOV>{_%S(+Gvutc(Arg>r~t`(k`aHOkI`u(y`v%()r4*+D$2qoIC6q_cbLms(~+)plReq`aJDqHhdo}0>n+bEY20~@b~#*+%Kj891K|a1TJE(fctnLDvoyG-efDDj1rYbVKLMt*gnwG z-^Lf?iFY@27}88>=aU!MHo7<3Te$?s^whh~clIofC*F--5EMhBeJ_2512dImP0-$J z66nSDpicF(!r!`+(hGxKFUUPinx$VD$*HE2 zat?!Ew?&MSO6wnq1|bI*$5my&GO5fyrY&0-b|ETmfFpj)s+5zJlqq3LI2c?J`0Xp> zo8oToD&i*qr+6$GE@G`T-o%+xYpXze8K$1G}kDY9Y)@C()dO0 zZFCtLNJ&yDy;mR7rSi3fiQGUwjZbE$u+ta*z`w_< zxxMbOj$Mu%wg+hwovm$)90whX(iS_n*_E_ut}%|5?qlAZ?oQs7p7FkGf$@QkA%A$7 zb_sVTW8ew42=`fFg)K9Q4{itC-@jXA9tPI#a>{3!5Ux}K7bZ#c~npMY;Z(i zy}ySyzejTKafpsd_5zM3_J?-XvDkLYvDa3?*3NOpzQ|eG{n$o?>Gwf(BCblN+cVE8S2n?#$&m?HS@7 z>c1QqAI_ zg5jaT*g#9)I!{k`Ij76s!*R{l!p_;D?Y6C-&5)MK*1#5T>*m;HmmIep1)URJliZy> z8@$*2m4Y`zW0hW77&Qg$U@hhuH;V5rp4Q#e&6ElnCKeKw%nZ+04A>2Jj`sH(~;{;1Z?HhGKFPV`PCYTzU&Kh;btnv;zPkoTW`oa2bx+c0?;sN0-KaQ(Ty$@+% zDB*Abs9n~De?D_1~?I&#(o7uk8HpyPpanGLS$l(k* zPq|LJy`HST4gMF@;~J;r)#@V^?*k8EBKw+4u zZ(%HMb8E|di`jC|+|(?YADN1pwi;i^t>hzymWDpkaeXcQMx7$Yi)Nt=Ux$0n*x)eW zz+uWyNnIRPf@)xc|Gl@2x0d^~>xApFOnf0(c=s{HE z(4MI?m15z3p(}yk{;R%)-pihK?o{^>y7Io{eBwMt$3Y3_14n|h9+mXJUF|3@8P6xr zDQ`XB3V-IHF%%o#tz^-LQtwATPz$Pzk?X*35VDEgbaVCPBrK&E3d@plxN(ZnYHDwq zYx|l%!IWq-&v@F0K?x3j_I)+!6LJa|jX|Q!7C} z(lv%h&8Pkf{|r^5TC`Ze;2-QOOg$P2?zWz@?oTe8yQ?d^d$G&pZcaHFc3*Lq^|D^# z-R6t;Hw;7t+k{?*wkb>1ms(GJA72EwU>vnYWd3i#B39Qu)os*|ka)vZ1EHg}huqS5 zSw3s5Vze0F8Xw4wjpOCKa>!8Buv^0VcKT1c>bf%GJ7GQFl|RhgWGAx&m~Su-)uB^} zf{k<*-LG9xbExf<{b7B$WN1?m24e#G0zqGH|0my5?@`}!O4%vzTyMN@w|BCyf$xm3 zseggLOyD2t<&Z5$YR?3RMm*2;K;`4i*ny3;F_w zf-QpUgO1?!V7pM=(6><8@Xl~i<(;xs-J|6}g>Y_ilzfiFtr(R##V+99a8r3s*em#j zQsM{kt~gFthxT#@-BjIf>Ps4;3ybf??>wA^9A;Unc!kD z9C*nLGJ!bp5j+{^$99xP{nl$yZ`27@L^ArN&Cw3TP4X zYekR_DVm#l68+Sl_)B}M#i8Mp(l3a?ZRq!JaY53OET*`^c(5AW2i4#xxDGyq$uK)p zi)qb_VP-PBnf=TzO4&kYD$|!~!PKT_38o6ufYC8;;bV9XZh;lybXbc|wgd5v%4rFcBGzA9lWPDbC+5A+iO+Kx=P z6#fej!dq}6j-y{KrZMnX&=QOS7XXATtPID%)MxGg1 z7MkHl&;)b?oxw)1h@uIGfDxb}_)hKQJ>)pKNxspzzn$U^f}{lK1*U@~H0Js~68)!r zegiavi9mv-U?Ug>bHfoZ3zg*4R7!?}tW0x?aL5mJFb(v99McmXrU-;nG@kAS-@$x% zAH>lwzJS5B$Jc|qH0oZP`kAx9N^k?10&jvoz(LUj!)Yo9CxGW*Js3}~XTc`$7F-2u zKm`Bso~#06VKs`?@PJBSGuccQk>2nmcnpqFo^^&*;5>MR)~3>@Ewnd}lMe6qyxT9J<`4C(U?5J z3&=kd6TnlXMN_hs`tx6-wY0}-!`k2=UHO&A1L$gCEA_tzDQDk+YSeFAiM+$JaWQfd zRl*$3iihJ3Xfi#Qk?d4H*T7%kIK>7Gz&~IerW4znnGgNUR4|>n0ZMV*=@?y4?%}Ku z!|d>?dI;0m9PCDUneJc^UJfRyyYPJVE&j$8*G|&8q_1W}RnS_^gpz11Xa>owEho*v zE_DY$Lmw zRQwa1f*mND;vl)Mjl(Y~Ej zhZ9wgnhiD~)kt2Sq zfa|0`tjXj7P4H>-2_)cqco_Bf4`FUIjZv5)6DERLG{SR@TL_wiRp1(WLJ;SK6nIJm;jiDb<9cH16ROWI0y^| zZz06v!FZS-HY9D~QaZXHz|+iLrXyYTRn{6)O0R&D>^F*qSPII}m9&+OVb-EPusIkB z@-rH>lGo!~uoGCX`N06@Z}1&vrDY8QR`MJbV_Lv>pei>5kH$^Wc31(b+A1a+Ox6am zBXE>zgYC&K^c7yFF|>&kN0A2Fk&*;ceJ~w7VHD~yo(d*`ui!UyfZfCjXAv0=#?_eX zpe(g~N>VNi!y`#icvUM2>%ej7J3a_XGCo`uR6`w6cIbo0=-lug6$i()?)1$OD1aU0 z85oEwf`!azG(Zc&vS17>%p3qWw0BgW)?^dmZ_tL~nx@0zG$JW7hsg}hOL1OTih*0yE`wGUZ`RI%}g5(GH*o7pHdRH%^y>zrLgK4-2X-X=RC7>wu zQxsT7EaDIF73AQ4a)oM#J183NV~5dsa0f`CBNVV3DVD1neHxFRlU1}nmg@r6kT^1q zOrR?z2mFeM!jt4Dn#O#hTzpD*2NWZx@p+1#>I=5O9~52FgQ<@&k$S#T{+{1JrZ}DyTkSwF?itn%> z*hbMt=O`d@Ip_p8;Vsk_$_%dH>?9E!r85d)wxO3aqAR0#rVShj8SV%y1J}Off|L$!pk@uHPz?{a_(jM^vggA2Y8>Rh*OR zTLb)?j=lU;D-R)C$y;ayFK}jfhhiV=li?H}b`ZWKMeukM1w60@GX;L6*uzaIL`O(> zSf7cZsI`9-2;vzNwdHccf#@vNmUF;2igpZ;zo~tX@ML(1%G`Gnpt!1C#LQL(i$OW+ z=eVe-2iIaWo%GF_kwN8D5}xv#Z)5Yy=l6uB$vn@(m)X=rgWDu{x9C1N0uZ zhfUxGic8BvQEE{n4~=B|sm(Bu>Z6DScAhMw^0=AQL}kzzW+W^^@tN&lU;Gm_gw?4Q zf-oCJoqZ*_!E5*k93b1F0nZ0ZU^A-mgA^nAmCAsPDaO1ZvuW>HVJC8!qOpQxEICMH z{5THGrq;k6@`%#=mYk+2$@TCgzJM1%E4)iw_y~Lf@{<$Pv+*D@4^aK} zg3JT`@i|fuw5N98Aovt4A|4cl7m=3WBjv_DvL6R=YpU5N(pdLVszEB#=%AD0K*xb5 zw2fUUVzCSzA?IK`6Aw4Re00XD1y{lSurhFf8O&7J156_qU==1mmGk~*OkyiBSEUrLf40};5YK;%zHq=(pZKJ|_aOu6h7V|i z&~NRwx)=4OR?ReQB#+T3iXPZa`eP3^fbH-s)!(JyE^7D4%o3L4YVu3j+w4LnhBFEm zxF9bJ`?%ixO1>R0^DTKluW+|{#LeOgu&0=w>=-sL`;5s<$8Ak=fm$6RSc{*a`D$}5 zRjsOiRSK&y>dEl0aP4qvC|4*gkQiDLsvNY2vV^7uKKhphi-gN5QK5etb~^~kW(&`aM({7V|AKO>DW6gR|6 zN5peFtA35H61zf8@!~l zRSLd^OBplQlIzKr6}w3`{SaxOp^IU@;jV6^VT`G;<(z4ZajYS%e=4O(b&Yq7(+pko zM?_g<#WBJ(v4Jp}|H7_iE>rJBeX2oQVV0sU6z#p5KU^|AFz5=d4lWBE@&jK}Z+6dE z*I9Qf>H*OED!J-7PP!huzI!V8S9_KQ9(!_nn)++_{b4@5Iow*wsrE&0&?7t@vaFFC z&pqV3=!x!^{tWex^fDEYXPJ|!r)QWoiblyk$O$yEv)Jr6|1=~@-KDNlGF?qC)z#Di z{v~^lQ<&APj#>)kK`s0YS3yVVdU<%LUU+M$Xvh?d^>e;dcO!R#>yvvY_4&NCeQsY=b<=vx*qM6v>`}$6yUoQ-8x3s@%k=y74|F4SzlB?TDK3_& z58mPf6zxr8W7;)!o?;1|4vq>83#|#>^cMF`a>u)%Yq{gTvw(AzZA)r@$61%`e(XA( z7M)7$-yH?~1A~Qq$NX`gO`g6%LH&qpgZWeg4h9qp$qpAyy2|=QX^3>rG}GM6M5CRS z#W9bgYDd?KX&I9ilgBi{+Q-@;x>uAYZ#P;Esrq{QHu}!uTA?ZHhx?$L>?QN@RO%Id zN8_?rL;b?W(B?o^e+TbncRBBKS6*kxvC6i@5ua8mkYGj>b%&#xZr?HTeght#1c02ru-)TA}O`SuJ8R3oL>7mcT zss4q&`ri4}-?hbA(#1JS+ArDmrank&pM1l9(l;+~#_dhsnYbo(p)*%_j~0#dD+9fL z_x|8*V&JptR`TolkL+;%Fa0xJaruK>#`-w=sQH;S&T=7YY0PuWwwR%@AZ~H&P&1$& z@$2$WOHNZ;^K`>5z6i&V5p*>=n%T(wK+V;A;hv#8N+A3@+(GFW{20tdyBJ`Fv@fd`fxYgT9Dd zUN4B*#GU*F-B^QYZfdP%J!+k5bz6j}Etav?XzShRiE%AsO2<5uKI$s!FG#g5-{tyJ zBYqTJVP(bHXk;*%+(r4-2~_rfhqi_iWxiI#Z0o1Lts8KIcqZ zR$KnW28r4IY;Yn-@Xt>|$qRq~@|bW9cAAc2{sU9J*x#1J(sbi%c@`aAImOdLE?H+> zPDj#9i!u7CWm42b(_?c1^BK$2m<}Mo^j_<3-4xRcUXDWxQb`i07eP3i_^d+3pWwEMQFrmMZXjHf*HWtK@T`s;bpI=>xh zaBkpeQkmotDGPmVm^ZvzHw84O(YEQzd%l&t!Pv_v=@&|)^v|UW@*8Q7*>9a3Ek$Ry zEHpni$C?_M>X<%6B}FmT9P$zN5}gN3;tpw|5XZ&Bhe}=bOz4?170p4Hv|Hh0p#j0| zp}mSlMQTSaw^~&D7%mtp9z5hb;*0ej_6+wNqJHA0sl}3p{T%FSM!K*X=#_Hd_maeJ z_IL2CP}k_z9Sr;x81K8uR4^sT4oix@s6mo%=yREtQm=5fsOgqcu@#~i(>wD3sjSH% zUpEbnNihA8Cv(qnU12V_TDM5}PHw|~YFFip;t5$0QY$MJmFK~iidSi-=F_@_TWS~8 z4QM1bt9z8xP>w)M;GDz4C{c9VM!^QTovZ2P;91A$z+>+&P1 zsg~W>BGeS-Fx9u#GG;TCG;WtW$yE(=4bM#`tKQl;x~R3UC1AQ~sH6WWB^!QO3Py$X zZG;Qt1hsp+F%P)}7Lr75VCYh40L6zLR};dY!au{G)B>8Qolt9POVy5aL(gbZh4_?} z;n9I2{#O28o-^L=?pBV$j`NA*6ILcA2fni&A>f<)yF~&{s;t!GQjFzI!?-TtYW~Sc zkZM}WSvOH1e_iuHYO6f~ z&$vQdcJ`s#HgqxoL(i$_ycHUz#SBwS+4MDo$3k7vG+n8v?dB6^ z>gqSe(+Kl><3rpG*)R7f|MXQ=PAxb>vzvzP|=s^7FMWC1+S41%}N zr|{tLm~a=RF{-WkRFj&j29TSi5-UXwUq%H%8cd=)9Za}_>Itp){qpbknrSq$jNM}| zpDO))@jZ(jquYE%_&sHLLYrSF>_5OYib^YF^ng~OergQ2+}tfnv7EHrHY=u}=`YiE z<4lua+H2lwRV*c})s4Ayg@tzPP*IZ08Y{}P+0|NQ^{2Xn?vpUnb%~~mbVNl{OhHqW z2j5i(Y9FYr_!jbDKPpRoI>%vi_A$H#5|J_dD)^6ocA&Snnak;Dl$O=LB(cg5!_R`Q zVc;XbTa8ZYk&q>EhBq&}(=gw%#?S}kREDB$;$iFNs7lr@=3ge4`JDNgv5c{^JkeOn zGSocMyvtllPSCm7p`fuCW8md@DTO4dmne382-rq_H6QU|?Vh?;xu`9}naLA0N~wm5 z!ZXZ47Lh#ILsyYkDLOePU9)aLYr?ex#{<=T?>%~_ls3X%H#L-0;^#k~XW2XAyxeZM zBe`k9{iKV|!OSwlbn6pyfE}Qe!XL$7)*8{OHJ?RiU1U*=b1iYEG4d7TX2T})7t0>g zW#bIpMy51a#r6`<%HQ-UU^1$#v{D5!8)X6vS)=CGR#1yD0e8ovQLHu>Nw7WZOXEo%e#$Ekn8Lu4)Prt9bNaX1bLWS zY#XLN^rNZaIe{hq8lEV(-F7HtukCeeRPvI9?VsBHDiw|cletuH>0jSdve|8FA47BV z@2G>)HCzFV;g4AIL|dX-SlXMNQFG1DEpN@8q_y&C3CN?YkBm7?yY$1D@pM<#Iew}h z>KVSTc1lfBsij3`B0ypYQVsY4Iz%?WBs>%6LbJdyZYvkVU!ePes#4G07{&=bFbqnl zSA*Yu<$Z0ulB-TCNj0RNO0Jny>g(%oH=T#jCblZtn*1$sy{((?2WK+ox74>(;}4*x ztV{peT0Gif{@dKpdc^$Bvci%@o+wq8Sn0cIyt%9VP?kg&SVevKUxaAGaIr1ip;XkW zpjhfF`3H3-bI^J1E4f65GDF~8G?OBxM=+Q9SA1h`D%eXsDJ5Y8<^ntq%iyo!{edGi zhU9gPu<6rGDJ1nprWm-z3v2&%_oRuubyWtpdzCMOeBfR!dOUA?SW}FwF zrK_#|xE3e|`!U5}S)3W$sP0}U4Cg0NzhD($r=Iouur$m8R-#U!O8$*r!PCN7*S00O zSE`h{|M%I1Tc6nEL)7Bk$`*Cs{oTR&!!ZKfF*uARVor%m>6&wZ@Ys6YQro=KSk=@f z>awYlrJ!l1?!9zgn58doS!(QV?xgzx#*(pk6E{dVNIb_rr?_u~WZaMXsM~?3IHcXf z2cd_p$Q~wZQD;0Bj^Qc@wRr>E4#tBfbj4o)c7<@U$d(NjxDaO?HDZf*j z|BC%~@OvruFC4`@)$*pPDR-Us{rmZC#){@-a~Dn|y?KxRuBDVUtKpsTu1PWdjPjZ; z8TRO&^GkFG<-+EL=HaqexD06c6Q1XL>lX?$s9*FxJ__!W`_zWojq;N@cop!nb66Gr zLJ!o<_#o3**dSEk&cU8E0`rvm$8*zIh8Imz9t9fu7I-Gu7o}86U6uMQEo(CV{_*S9 zG@p`#jt8gXS4xaC)sqZXN|HR++)Wh86E-OHGtaR+l2#fv%QehyYdgzM<4Ij*ajIBJ zDr$aX`eN!P&1YNFHC7j{hOVS;CPnVXlZ7M*yMXI-1^rHo!^OaG262D0tw>IERC@rD zxJtrKzB03tdUGvg253ZO>>BDAt`TzjTl(_2Yo?iP#?(EjcalRtN`H5x6b#KmGstn* z@wA}hxAPQA`W<`$nvXe^%NHiMaP2N?#RFdNAWQiGynQ^^%rmCwWuAZ^fGtpN3Nx1k$ue>1h< zXo}zNM12mE$t`VuI6AyG81Jv;`C;p9PqBSU8JE1{C;Z;ieme9}%MMmJU1_RwjpHn( zZ;WoYu@zsDJY;(ES>*z9Z`~c;BI%g%tvQFap(&uxEf(gR>)V;~THcvQ$(w`)Y&;ic zSLiP5O0j8lcIyml^N(OD<{#We?TxnLTFeOkAbS=()9$N0{sdogo7lHZG%(XxR5vn| zB;uax`S8imm|(fUCogdnuwSw5OnI8z>vygn#q9M%XO&yznd?>BX?F=nv^tO1Q52~` zxP*RjA6SE7qxX?_AXyQRkK!r)&U~)#ioo39+vsv}eaHglrEpW1 zP5LH`;U`Heq|#<4iZ$Jk`{>5;jC4YNYrbsRE6>uu5~_&b#9q4nVlnP7wife->B6V7 z9%eLIPNSf)xE5@}=3_R|{{5|%!bhk-x;A5_{>A*%?@V_D(NR!Zvxf2qCx$xt9(ZPX zy3zIH5sK|Q`|Es4Ht*?RFC6skbKmz=bL0%p)VjlHF^!u9HiNV5FkMZ3Kq$+07rN;; z8+KY+n`Rq#(ftg'mMxjJ3{4={Yx?b8j^7m>2-EAUC|F?J_2m(#c!+$FX=xvz<6 z8Fs?;6hBiMAEB|TWV{&6Cw)Lq5~fJR1Jp+ShX!dZQV0_@N|G1}+Z=83IbBxQB7EU^ulH199vWJ_6 zWY5E(%k|Z_NBtO{06H-@X>{ZxbB+C5zlMw9@^BTn^I}=UGx?}7zv-G(R_aR;W?IA9kq^1weUFq&ACjFV}V#>(o+Dq|C7wc2Dw!!a8r6Ks~?R^CMg$ zv|l|%qXvtpUo@Fn&AsC~afx)7(-bb3xXv&`3d<`E&7?wx8~O+GWBI#W-Z)blAzzX6 z$O&={y;+waY~uF_TZCcaOrGVhFhAgJrWI^PBbSA!$Ko$sn(pOoNxIPJ;#V{Yv3Nc5 zX*<=L$~uaOnCEx;Hu)dAOL&*M$~hJ|_NOH||M7J4+CrziRsHRP4?N|=-$QqUU2$vm zfF{7n_&bxpsAMo>fp-}qToCT-TS=>Q6%9)$qT`XgNjfZNmw~iS9&0#ZfJRQ=Og~f? zC!ElI7M2S$#dVyGd&Tu-j&MsD4JN{aH1@fY?hNS+E7EwgOrvY{!EAgTk3&7xIJFN& zaU7&R#u9-F7V|6{njp9pT znQ%ZD#$V@MoSl2mHRl2p1@e(b_{+00`;BSG-lE8aM7q;q6OEg9r}&c?x?{B(8lv** zD8&{U9f}Q>3bYF@pueH1lCu)sDf5_X&pqPm3hnqq!Z_hNe@7V0t>M>l2e={p6!tT>jqb5p z#IdZK9mqCk-%%9DW40f6j&fGvOuUsZ%kSjwu_xJ8OlQUguY#(u5#{Uvy3@=~BgMT@ zc~pR|8TP6l)KO}2B~>Y@bW`-oSn6rar@T_uEA5pQ>O19)>Q!SkuV$c}9DtUhF#3er z;w1DEt;Q!P!ukdEtgoVRS0|NKBOZ%$&^-WgxE4N$r{if@Bz78yeoS%ViJ&2T0vYx= z-OoLbyUmTHD4A9KDSjq@njgh$d{H4zSS;M579vCUgl*=Zaoe~C6xmsoJIwxIs z*LlW!DtMcCMDJVA3ePNWJ?{tadEa_pslfKY+hCpWhVTltzt#d}p)sNQa38QBqMxXL zr)#J?FW#a1r-Kxo{FGS>+kp4jjV5dDRlTw-^ey=Rv2+%2R+R7Go@aW_oZ6r}Bm`;c z?uMlWq`ON}0Y$n?0YOB%YiU6mq`PCu?Kpj=p80?Iz3*o~>*D5|i6`#+y05!s_{Y%V zU@$O0aL+&ApXJN;b@I*k<@Po64f6H(z3~0&8-ydC6u1)@6K(N;D%zsXkn^fD-t`V2?>k32Ydb4| zq?@RQ9X~p1IIb#Xl?3^LlquE|n+UH^!*?1vo(cAsn#?mscfFCeKXM?vGxQ-?J2*7J z1Sb0j`3)S|QC}TjqHh^u*k^qAeLo`(JTkC5uqe1CR5rXIGA_DSd!xsgvNajIy=P1f zOzcJYQbJg`E~ZH}<$B6pg>@*XD%hw7)RNBC&P^C`&pO{b|8?$h{^Bg+{HXq>=2y>R zKeQY4tbCykgAHg-1VMq`H_r`ZuJ{4TUCG&xv3*db6C=W>vL zv_ID0&0op?y}zpe5C2)eBk)t;RbYOwTBvY1DV!29qM}~K=!$CDYcycTl4@)bt|#w+ z+U2^q68zaI`G|5#+2nZdD5gGCPpb8tjhv&Lot(X#H9+}2SC^|@)yIyVj!()aMN~S< zpCp&`y|@t-nISHlHAoa*jwm&tqzM@zeV69aHbzpyb;1ooF`+uaXkbU6QNZ$l#F2gU z8~(@sZ}9)u0`b8a!B4?oLo>pk!m)^zzR%VLQE1y3hQ}}|Bm~Yy-m8p*snO6Z3z-$;xybn?aDu?AxvMo)P{9=D`v(QXf%opazAeXm>T|}CbAK(NjS_m|1tLV$f#>j$*97zu6gu&54Fxr0_A zcuVGBW=({NWjEFti*YuvUgpkmUHQ_8mR#WL3cZB-!UtiF@KOi}29E!TFco=Fou9ye z%irh5a4on*b{R{-{S6=uhyfPh3i5ijm^+9L{c0DpUG{nUoUW&Tfhw#*d*cj$ux?s& ztv{^ERy(V{RTJy1%jRaYr`g4Qjsn7s$&>0EmRNT3on3ohXv_|2V&fo!C6F;9=7y=VtI1T%Pj zTAs#IYCX3;A^vs2x@>K;t|D`G#d?c1ZCMbL3+V>98w%K6kXgS5`d|Pv1Uc>9aO{9Z z2Zy+yNoTggMso}I#w}#O?=xjc0vU?8v1B^((L<4i&xH*84KNS=U=qp$R`WhgFnh2Q zS`F551@`ZY?KvO_hG1{m7wgqF$hMaN?IYTLWVT=9TtBAg=`B>@U&s4xyq4GWEqw>x z!;k%e1dgw;T@S3lIQ)(|_FBZ`FW9fKrsiQgstU7HXRI(MFu!08u?ghq0leM8u|8)m zz)N%r|GI;tzs(%NYgx|Bhk2(JOhwhofX4P$V&JAgZ>4LYL&98*Q{n^cVB*>(~Lo>y>s zK1KBTE$m*|pdj6daMXccsXsFhyTw)DPTt{KXpBNECJk5S8uKr9R!i{J;W#4+;AU&U z(^UL_wk-}`tpK>a)c;vPQ(&q}hU+RD5%@67X?^19yF8Hs! z`0N)qmjG|m7au48zyC`386NhlFP^Y3{L*XC;F7FCR73-Mz*2;Bo#+B3`-@$8aBX^DBJzft>|c)Lqz8pX2|Y z;_bfu9`A2(lwtg)RCvH*LE(IH0E&3+Urd4q{?7piS`?o{QWJmYi(54VXUmT}A{GA% z;QmX;zq0ZE|2~tB?+f5J25>YQe*TNk4M`lFzc2o_FXuBitXpyaGorcv_xUepISyAf z7Cy3A{OvD)-SPjw$NRs}e=(@##`(_&Us^$YCLZ5Y2(GcJc>6*{R{$qlj`;@nMF-p! zv*GJ{!$_nKSxZK-GljK6AGRqug;{(m>{}afemdjpiP$msAcff&p$dPH8^Hyz_j$`+ zV8?T1*y-#HyC)(+&*^QZ58a8oz7L6Fwq9sVrg_O*ux?MW4|qVkgTtDDb^2H`8zk9m z<`VmynZdjyE9?~{h4~t)xXWY@dy|wDin6n~sZ;?s%v$Z47hFHGTEA;wHS(KZ)A!i- z546r98b6L1W{$K)jH#2D5s0J}BCH)}$I&j`@J47bqd@D=_95UrW+pobKH5xRgmofNOY!c$- zNpuNfg`-I}=AOBv2m8!CMc*)$F@wsW<|kMl=4^O|7l|V*c0+F|v z{f0?kvtX*rZ~cLsNg*-|Y|jlk&)yEEGX<}@nZ1_VZjFT34wNN(&gzG&_7+yTLgX4b zg4}8^tPf5IlQHf!U|Q>=arOIf#Sp_RY99h~7BtMmcRL<=y`moY!CfYr@M z=Fs-A@#Uwx$O5cKhQot+m~JPp5It&24&gg)65XC}m*I+IL|FFq!nG_1Sy&S+fnZh5NDw0B>#+(%x)PyVp#_{XU?V%?CPjeDNR&nmvsp==L>QKE9^X0 z1*;uHaF-jXsl3+m((H_Ksqi@dN zBK53+sI&i`)Zn_o6uF;PgL7ddSl1A#@x>%c50YbFUNslAuNxJaFgL(j#Y${Eonyse zt$&dQ!QP&v-(hq)fia-~ZOimvXWK=YZD4c{lEqeKxC=k*+ZOj6c*g`a9-|810HbfyH#V>^VDflsmzc7A?kZLT&8>tTw z_7E~G_pRYrVN61G(iV8YE}>#*HW-^VOd6@c9k(vh2JlMlBCo)AenbYd6ug}=_Im6{ z3{<8JKuuJ6`#Sb>joBNxa;q?wod8E(3*QmI{ZGO6_MoL1%`AZHGLFn7U(rOi8lLw6 z?S$Hmt>9$_g7tocS;Na*#ELM7DNL&1>C11Qp-t=!_$!Ox{`tZdkHt!*KUV^yQYq|U zt}vs?O}2;C%r3;})(-NHJHdP~Ygykhy)imJ=Qfd!cB*-u79-QxbhbaZ`1&@_UbBZX zn@B|(Ykh+e^(}adr544AcmgrEe55zC!R}jS*zg`S4dd7fV$rmH`xbQz4Y!xDn=vnJqtOhu?7|8`+oq#pGff1!1>dU6vldXaD zFOr0(Mj2V)Exhn4P1bfje#sd9~ zxY&C3amv_rv1%`Y3Zp-`^IL_2G6t;>B}*8|x_nm0UYVl19gMwO6I23zz(~9seEBHs_`l>8|6@Ml8F<3%B{ykt>^@#&7CHq| z+QSq^jJ_GhV?xsLO25bKxDq?XQT96KH62C2*m=uiZ?Fszf@gFA>R%f(9bku@fIKBg z37qFg;P^GWIG(SO*h6lI!*w7tjI6>=J~wtHPwYRj^7<7sRBlwwG(sKVLS{NyfZg*7 z>|a^bl4fH)a|Zj}nb=pK!p}^^-4nFuqK2^@8H!bZNxL%YFca}gC)>I0+tzTazf}!; zzSme+mNbu;ytUHWi%3a%tD*VWEM@)-LcW)E#N3J4{V&#PYmr$Dd4M9;9P5QGlO1*y zrWgh59-{Vp z`0~8P_27R*y<`{6^ch%}uRzvulbMA$)M>r5&S(WA36XW-uHnw%E|GzuRe`sGh+hdc z4z&w*4lWPIh9-t)gdT+_M558E`T>23F%7<^FUzf>pJL`UQ;`&7(7n({wJm!Hp;1@Fn=CQbnaRs>pwERB_#MmIPbdE#|kF3!YQn zYo5+AKg7)QK5{olZTT_x@9vlCD&;4sp;SVeCkBP7{Cai?JRE=1lc;PiVsN$~r z!n1>;g9QTR{k457a?O=jjJf9-Igs-H4=eet!`;qIVJHeY4H$48wTy=AIiph!bxvzNc z#@2}`6T8HF*;P=zCMBUNySZ{oo{4Zo6Z9sN!q*YJPmCEG|&+%n%&Aj^YY?hIF$lomTGT2{R={uZLC$uE8%dSN>G66D< zz9&`rIQ)eoj=oOI{n>rT`_jES=0Mz~*hRVC#0GNR%vCtxsB@!dWn72Y;m$0nGP@jY5|(gLo^maj zPN)gUtzFU1XopeZ(LS;(G(Gr-&yOqjB6D=c-1IJK4bpd|WPa|D&E!lA%*o#SX;|v8 zw3dE{UebJFyo_!S^bI8F{g@o_jub1E=1Yrnv zog%T3Bf*n_6*;A{dZl~PTcy`bADVtUSepnC7_xap%1YRYmG1 z?UgGy?klyFk>XZTjIJ~enoUu`|JfRBjy2lphu|6TN3KPG4Y!Fr4(AHJ4t4iW`|`A< zY)k8rel0yCbwS{2Br|$0IQ;#rlribgLQT0>Y%=F#ZU)msoAo7pyyuOxuB)oN8}V{O zlGLS+TJG*~U1Kxj`+6_A&wCp=^SSQ0!k%rphR3vUR+L_V1e-6!Ah$D6T+g00%NZ;6 z%X(AV08gB4WNHUcGjTd9Yu`i`YOS@eqQOW)sF?q3R%`f~mZd++IGfQbZCMU46qAJfBA)^$w&B!BPl$|;U=$})G=SS`j8*Tg&1 zQ_7vFCMZuGP2G25`g&VBD+&2XO+*ahge~HCLP0K@F4RA2&7$iKw>8;(WL+}bS$E9y zT7Iouw3yz_IH9lA{|i?QZOvJc^*lY0CS*o3il)!V5sbm06WXS~c-JzuT~2HEhVowC zEM3>*g8d_JnM8N>nAx7FT2*Q4NK!h<71a7p(GwR}-8(i`0ZF{cNyQhE*ZJ6U#M|4o zRI*4>a2dDBO5u*UpW8{#=)Xiu>!0)-bDo)L_|5i+YS*%|^s0Ih{f;)-oT$$;M@D0V zf$aE<>xU01%d(#f>+|++FY8zXuZ1X5A@~g~NR!OaZanwq~ey)shD*99SY)-G7w^?@P<*dKcXQ#eO zsiCFN%G~7WUrBR5o=rWZJ$L@8o_0>;wLtGsx>muF7*{VQ!?hglvHs4V9T$|&@&NS# zOhwCMCwQBBYIs_h1B}Knu1*iNc=O?t zlHS%@R!(e{*T6u@-nzMz{SA8UIRf~Arxf9|S#dttFQ`lN> z@b{Gq-tS#sId(E7t>qwcU$GVwLCx(>qd4sj^K}jDVPu6~mY$+r`0ivfTwfpZ~D-*;n~Sk&iEb{xm(gyBYFSbhh*6B1?jW z!sE0txg_uC*cz_;;4)s4%Z|I=tim^*yhN`)H;9KJ8k4Bro7gDca zmOp!GR$O}Fl!6~!!Rq!`+_zTxhu%rv&;HOEIo7+o!8_~!a;emGtJX8 z_Hk?-?|;raqRA8iZFt?2>@lU@>@k{7XCkxJh4}{6$+3tzWW%J}+U%*_)E*f#nRtEy z_W-N+d&UcR=58ToRK>bxYz}YrC1!t>nU>iqlcqIHKJam9;4D4QW|`qn-QGP&{x(oa zy6S4~ib^~6?!jtWX`!Lq;U5#*XG z*`rLawj!wdCgc>$S)a8hJtgJw$KJ_@wdUAi4~&+0H~Z6}l*Fjuc&aXSCUPaCyMsTO zljJE*%efuqvP8JqvR$Rr`CLim7v;V)Z+t?`KXENR{iU7!B=)6}>h`&JtKYHtxg^9+ zyKoihb<)Vrg>DW-u#cZYOGF!LJG83wXRaf6h)tlk%}+)JYcDE5y>zG{hkp#7$yx7f z7kHSSm61Pr&xf{Ilg$B4IyWGv*t^6OSNd9ev15T!Q9W%>4YSepRy)j!i%^F&LhRwr z>$>DzB0pni%IB1~u5xj!ym{j5xF3oRz6<+CZQ(89(H#lMK8|E3!Wh&CT{LbYZcrIn zrjDeo^{qA`Izk)BT;V(M&)7I?snNtVjmC&{m9rihtHTL_^qfywHGC5@dZaH-9rf|s z&_~qgmta1m#D5B<2eSzORxYjdlh^3~gxYEjmT`=BG#9(@hn3%*qhU4Tg`2`qWtM!- zdpBl6Y}dHbj``vm{wL{Y_eghF*G5U_k0PQP!#{-qz<^!v9d;Kd5xs0;^w4DH=q7X`zw8= z)>>F2zLc7gPLcEBJytE@lYC8_#TF6`M_Dz)nJpdR2g#kp*Xn0)pO}vESKSZgy+Tp( zv7?`7lzXhBl2lW8!;ckOaZTA*p!ShrVrH-}!7OK>{^qP!#BRaH@;q!FVY9LEhxwv6`@&x=#Lh6-a%+{3JyHY>Ga=8>G^%vT`3UhzZpm!U*` z60==uDyD<}8z4SXlfVn+5qI((r806}7aOxS_J`P1b+K4i)aA17YIm;Ye) zhOA9#OHy{{e9~0odn)-#r=Ev-ElIQ4RpdOs*D4>rr@cUjjNANq_$*A`rL0g+se8og zLVGbyFdXYWL2swn{O)5gd>xhdtDW5CU2`40^0U-i8YpFI-_<3(X?tKgJ8ArRmqZL?4GM{Lf7_-B%;4TxO}1cUjH!YSAs$2mk)m z#yR=2PGin~PHpyYD=tE#znFh>)wnriAdCRV+=8NbKvyOS1RMu}s ze%1FFCAiM;gDCb0(ntDU+ALQUUvNK&d-(x!S(oZA9@EKHURe)+R=RVH`!Cc=H*pko zoOHx02c<#cBa90(;Q(zaO%y(Hx6o(c0li>vgtzDj;wsB6k5$n;X#8wG!^$-)R5Fwc ziOULso;hzb9;SB+d>gJ9@#vXZ^K<(8OK0`aOX;}Y_NlNOtzjk@?O+(6X56z^Gp+a` zNbvR+zTqcxorO)3>ip3absbQ9DlZ&U9gm%FoE6mfs^<9M*rMK2_Q~y~0ek|;zg*&T zX|V8?>x>>acbR@E@x;c#ot() z9(a^}Jn%Z}U7$zgSwuDr?c2y7(ZzZZtFAT69AdPi35WrIC!FJsvz$`ro72~*f6uKSi>5|qMiavOLn{JSPB7X-p#gzNb&XKSb__oN(V3h`fnDLB;0Ws9$SrHi9F!4KhG~ z+6&<55IKWyBuxirc2+tmZCAE~a2xAr>qvE6Q;9lMO;ERE#xmto$_>Xj$5iDTxxVyF z^oegk_LdV53v>7ie1C2aOV|hYc$x*PSz&Xd{$BeSc^$eFyb|0WY8(0`91M&M#03ZX z2Ein^2KQT@;Jx7IP#qjw`SA77oA9fMA8c7$urf!@&DLT>fjW{bvIr)&ANc{|b78P_ zK;D9V_b^4r3UZhu1H9k}M?J?4MO05ZR;doPo3cz9E8moxDk&KI=1RN8<6?PX6Mu@U zjSSo`hy}z^AEG%S@OhQ6m)sxT8lHx_?7E>>fq(q@fFMcO~OlTvn7pKc*<@QRfqo>kGt%K_O zWQXFoqn?37;E8&}kxL!yDCTJF_)GbW+OC$e1(W3l(1V}2M%)E>ygW=-y1{ar33@xN zY1E5~WLHE8*9SkA6^I0W4}1n=?g-2X91rXbR075~KKwDFL@T1kn%B1) zDdu3TpL)XKHIQw^4dgfTQQ^5bLi$xcDE9+}S<5jQXDrz z4m#>P#$e|)Qof37dt4a9uj0M~kLN;6rAInF4$CwbFex?jZv#PsyjL>2j!r&6kg3;{BmwE z=-TI4OHP5O|FZrBmW3M8!I6dGHDK=g2c=+5m=cEgZ&#~UINIjy~ zf;Dw9j%|W-lCwO##l@VzsEfdZvW|b0<(R_)7)^HYN4Vi^U7|3{;POTR7P!DS(e=@o z$kgyR;f+BF9*T8=zQGj#p}=>6o&L9hq5d)fHK6;)2fbk1FM?I=4_CyB=x1%Op4*&i z`H}gV3vbXPti9IpU0}kfAT5?Q$hv$0OyDZ?gqVmb>U_?5&aKXE&V|mC&H>Kh&SuWv zz=A$ola*8><=d#9UQHd4e}Gj4_AS$ z?8*&d+Yxjhra9=a!kLfs&su%N4O&J{!|&NK#D!W1+Xd?ck^|TM)dDR8?a@(V4bI|) zfGc<_7z4Jldw6xEPPDr=P;Y3|G3Qvr=^*U8ws_*bqhyCgx37j9wgcm&Vr0DA`AZxuBe zvyy&aI}Xace588#4MsRU_$H7V7?1JI9VilL8d&bX79fGgffB(A@MBI4jSb(%iu-zW zvGy8V>R$7{wS+#iFEOj&9bLp7;};0E#ZJ;*X+2nJpEAM`bR1H*tE2H0EOwgCgU-j! zQ&E1_>XXn{|C;=|Hx^~33usdE7kOxbbvH~OTRP{etX9qaHh6QzuGmLw1p;}YD?`Y^a zqeSG+@-XR^_)19O^YWDKj@(jjcxa>2#AMSdr{mA*=QrH@iYabadSEdMGOm+xW?{88)gV+NdSk3LT`-=O*6SlK*F|5XPtgVA zTVtf58)eP;W&y-FA6qTyUF5P)p_?S!Ut}72N?NlE*ch$@w~-5TW%(JXlX=Cb@r{KB zLW0mmD2IxeQ?TaL;a_vtxu#rA@Uz?Co4X6k-(4mhbm{|mlInqy{DYKdG_(lc<5|WlgjCTIpt0>zYZ;_o$&eXHKycRG(<(26M4>%Up@8QQ7)v zowZKGB6Z8^L$|>?dIr2~ckrI0kvY4|Q~=Yn7+KfP#1Chq2kuo5+ZkMbL$)c9!dmQ5 z_G|c8SD@1OJ9tJ&49{lF(*rxon{_F&MpGeJuZgLmpL{A_dPxm^Hv{X7t;+n5sE ze?-NV|AQ2VfAtw?s~PaZjv!sxeel1X1BWvcezGE{h5v`^2w$k19m3QmQ{c_33&IZm zI=d!vieG^g+X;{EK}7Hu)8)wG-KKZ!2!)ps)MhKNu@7wz;s(pVP?4Zvzb380w^k#M z!KD_!p5QU#VP_#%IG^0IgMda88G*n>#~j1+iC$KiA*L{K+#0Yo$B5TH%@hGecpmh_ z2lAO#CC}`F_8WGt<)dZTv2d#FpzDzDtW1;a9XJyPOtAHAlc|XOsGBjYJfJw**-Obz z^A4!p1Ue2B(+%r)CO^(yJTWo1yaSG1MjvP9>)IYfO!YkW-=+s9JO+j4>0S7s2glY5Lw?O z6TBU1Y+fW8_C70(O$I?u5kFZ7POi0e4w0!2)^q!7^sJc+oOOt^*VM ziaCi)xd=8jj_ptT!odK7hRL9Zm@%Y0QOv{aU(9>s4)+aJt@%P>7#iJL*LDPi%xr6gg#`d{t5T^78FdL zhGjR(m9Tzd>abPq&Kxxcu`jsU=*YH|U1zmq;>0@U7IJ}%x39DR7~?r7`X{z!3-P72 ze0&SAiIe!8Xh-psJ<+_ted*h_z|OF)+WpZ9u(EZK?{8n$%L{pFS=ygnU>)S@Fn6@w z;yosRbgD3q`A>hqvD}_;g1m$Y2b#K%7=`s(j$pJQ%n&)oYo&;OmChCqN89pMcq=+w z_>(DPf{ElO8ddmO?Cr=?M=vW9si>k?Y&c%+YhBaxial*qkqPJ6JfSv@YRvc14(br= zwn5pw;9f4-x9t6N2p?};;U*biBpib8uH2gqai;oFelkv#6d=mo20P*nY534c6oioh6$ra zc`&G!k)$xU$>>gwpkFazf46*E-P~hx?gkR#P6>j)zOPrVY_3tDK=^Y^F@9Rc4DO|4 zyItJZL^-E^oti3FRkN~nrLIsju$g(_lp}++OYQ^KZ~p&;wbFR=Pna`{u(g;*i0f0P zxZFRSjIMTr>`u|WT+}@|vzJ{_J&FqX!A64mm@W&Z$y<$|EQcc|{MI_{%rUCxG*foQ ztxW#OIGt|`>~ls;ZMIDyoxRS@XS!=UaW&xsHiydnt4pPN2(brX-W+a0rdt&Lr2(2(zR4fvhBfli0!k(B78IwehD|hNkX_}fC z_}BVI8iu;IdO{A(ZS-L;vfBu{XVEC_1$Sh9vcuRzPdcxLQ_aQV8!f-km~_|MSdZnE z9L?dx3E~#(uHKWjb5f>gpaa)mOw{wa8if+I+OETTs@YrlLr>$c*tNpDJu|binC!Tz z;dk08W4o|5Ha+FK)g^D(AD`MT{$t$Ow+V`v|8;uP9G~X^muUSG3Ud3!S5}HXo!k;G z1*1YO`bPTGcpoV5tzgazJJpU>mFQ@>Iy+Gd!Q@=mQBf-q5!D~W1KCdFW_)+EpU*9C zb2ZN@Naw5Z%*F6eLI-(b@Th&4zrpO|k|QtJN74u5Nd)Fn=Qc7Wc-NlquBZJI-lRSz zNs+aDTcw!)snIq*(cpdOq|$Dh5_D$r{WE%~H9&*jW^CcVP$}a_c^Q8?+?OAs6wl_! z(wHB!E^0^P?}Tecipg7qsadV$d(LzInYNel37wg#<_S{FF3S!9XVRM+M=Qwhv>a<4 zDy+w;{X^$;%l$wf5V#mu$D|oY)$u|xtDyNOb~gphzvz#syr^a7m3THQ`#Wj6dqQZj znTrdPmew@(Exk#uvAofoJ8j3=v24_2*$8)mKIRLD%aW=JAGjk(-2F(U*jT1rQeIc# zyjdCXW@{Q3|2g_i%Fu#WBBsBtQiLC^kERpJ3MNL34=0BX#&lpl`m!7Y>{zoN)0fL* zc-Y(0DS9&ekt^mLm|X>NpYiqFo!E??F)>dDC$8J6+RA1^x>KI>(4ULV=VEcg8O8CZn0>V@f zzc*KDbJ=Y1N0X=3AEgJ^O#bVe4R{U|4=mF2=j{Ej-O1^ zbF#$+?x^P0&@)K=En2|*B(_oW1WOrl+y`U1Ua=IEh4Xt z$>Ln0SJ*G^A|#U&i^^5ek(Q7@#B-sp*ntn{`pLIROy(7?n5%B^2HoVGZ|?|gf&2P0 zXtrjsz9&iV^^bx5bY<*g%^m9MUd~jC{B2KCUTUkN{p1MNPSead_7eX$GdX;O{LZbi zZ}Ve9`_)Otizz2hxwGiiOy)Zbnlbow()N ze(Sz6&#t7^kXYnrlevZ5)96QYx^hXIqL)*55VsL$rSLn^DdHi&Jz9;nca|`ggIa5f72@L0Qq=MI6ccw-+TRiXj2kI<2R5*u4R^g50Y?0o-8bfhU197vu! zH`*5?OUz=zTCmQi><89AI7>x%F|?NUcK4*s1Fx9%YAdToa4CDh)iwI9ab3s|DhKm3 z9r-;>8FpR7f~i!rW=GGe>uITACyuHG(Nn0tC$CsMw8p9+B!{a>zX->*P1bm>HaE*o z(q~)k#N5n7J1-v}e!|a&FIwZ98ad`h+C=;vF6tzhqD#_`RzbUzl$+a&zE&gUvUb~G zNNOldiToFxpzIflg!VEqjsdlyfsAQr>7HcO#MTK<9VlYPgqZbGwXO z@VL|k+xiN9P9t#cevCG?+JpXY1;*EJ=ap3^K5)z);+(9-8z0!6d>ZDfzUEGIqhpsb z%C}K067#Q7BKnq0<(y=^*3$F}F1~@Svs0rh_(|L!fmF7(Sc0u%t;PCbrR5`MD9cZ@ zxA3cs>fz2bkgKV=J!`UJqq?t}-i_{do?)&=$I+KYB3+DX#!B`N=4f^T}FVqmn?lz%{%Y7J;utdHuNXXa*^<9BIABB#hBKGrC$q>@Qm zf^mRbDONDl=w2yKEEsJ^J}HTYLu(>UCJn3>`XGT5%hBrS6w4`ppxZo4`s;8nlc_>0 z{V7$XCAMZXM~#RM!}DYoOl!D@l!?gy0KGfyCVqx>-*3#c&+E11eWbcRhnpt8GZt`V zLbI&1E)R*}5qs z77K^++e4&n>@lMbYEznOYov*!7=Mq;quq%9!mT1VP@(fZ+0DN&>qVOz!}+%I8nbIO zk9>r@jZ|gg#e&95bE@Fr>RB7Dbo5;F2^YmRfthH{*`Lp+FOH_m^W{^%Jo;Dieeru* zBwAkFz>3-srW$vXj^Sd31bsid4jt|sTZkw9CG6BA^`*b?o+9hE9reDpQfQ<-ND(tqdk2veCM){IRD7)f>VZ-N#B&eQliC08y&ZsM}8x zozCS(i;$Z+C*>o*m>K#qxUSn<+i0Rxj~x+a?Wc04t%Xw1MLe(kW8{&!M|fb3Ft(F? z;vu@jc*+zO_SrvK99&{`Xctto*vv5@L|-Cib(F@iBlx{Mc2eM0Khv9RPvM#oq92v& zh7|crScug^eQPpl$am3yA=%sx&}IcmZZ=s5Ma-3>Dcmj`(NlIZI#EnB%aPk6u^#Ff zYzyHZ?DFeyC6IOZuzjuB94`{49EhWv(DXQrp7uVa9)C*j!hA#>S1+qVbOGxW_K`%Z zK2ygYXdXjR=VBV78)zB(Uvvy@Ef*9fm=qN;S5OnYNvO?g=3>MH^DtfbljMxHn-pL> z(RrrIY*5ycDO%84NDfiS+Dp>qne6KDuaVE3&CfIQkP>_!tD-^ZOyuInp=!*}y8^smij>Z zH7s7bITRHHdk`Cmho!tFnaSQXp21%CgfwHz5!Ci+A!f6E0<))O=VSI+#jSL<8#5l! z^b?4binN9Knf$_a!~XJsbr#j;hMB~k7JH#zPk=o{Cz(s^B4h~SO9$AMi1;jLW7)1& zb417Xq0+G;BlG9E$<`b53|Nr_`hwXfiQGzi9`*=#jV&x?qx^AZi&dSjw~w-GV72$) zUh8ipS<~6|!V3PmRY?D=_oE)+8g^hC$Qt`^RJWG}C%MW#V^yI|sAZf$ABs%z9+_%A zHR~{6^Sg0vpRlN`q|k*i*=$s@-;j;=HBk1+?7!eI63s2Vmk$^VjS_4cSJiSF z=h-m-md>2F~XpFvaE=3FD1h5DE$sA;@y#?!UDhkb9_bS6IqKKYZ# zlG&&h8O{F7f5j{{(r95W8{J^up&Gt9S#G_c=gAfRPcnuUBKz(0sH}TLs-OqR3~RjI zguR8BO%zwmZwIU&XhU{0canJQGV~yw2Pe-xc+fsDr!C7IV{N9uJlL`{Y8{z$vPg&#H;c=h6D@aD1z9P7&Re~Z#g+x^Krwb>z`P^KglhRFDC9W3S=(+lgzbmv5K6Crnxncn^ zANRpV{|E9l?C|eUgYwWWX${0$s=DzB9e1~AE2CM+$~+Eth;ENGLQMMm5F2U|=^f4; zX&EgLIu+O$qSy}!rrVeY!m+o0#q=XOl|d$RMNwtT@u$GoJeO9&FVaKlt(JG?cA*)i zI?Qvz)dsfUX`UkP9CahQvNcx~$3i);JX*OR-4TYu*|C^?!|>z^d}a%*;${)!fmTXO z)RN$lX&3Gl*y?NSx6o~GjqjD;<6DwbEI7}%+5aN@Wp=B;1pm>{WaONL(BH^;H4Uff zGthf!to4&Q!#rxeqtjVM8YVUpd7+i`tK$Z;l$RYNoIY1wPl=dval>PlxjH*XyQaDa z$7FbydHcByS%NF>D*Q2dWknh&EMwD&Mz%3$?L_2~_Q5^+v))^K5~&q#82&fZEYv#W z2&Vf=W&fPnFMED=`Roqa19B>d0y&vE12e0o7YKagW5bKXHACM={`Id5WrXT!57Aqy zBQwuDZ#_i?%0d{o8b~eV6#0x=#JSx0!jK% z_3?z9zbUH~WNYLT$~HK={^t6!PnbLCQ+3Yr8E1^Mpz!DEt-`+G{J<%HZ{)Xf2mgcF zaahK?v|w7-^idg4a!Q3;2cBntl|CkANOm-*PdK+eHo7^i1$5uL;9mU+>KO^@3KpPB z;RdP67nYu&yZJ%q23Jv6HBVEQoSHS(=J1s6QexCOaXHCZ} zv7St&eo|q%7rzHREY{N`+6FQ045Kk@&$+ZrEgG2svSue}>!JQpzF2>*93gW`#-Wtt z&jr&?1aC(&!uPVzeIA-xC9{~nY4nYD3(QNoz^SZ+KykAldsVn;=U_MZlsV505DrQY zl#R}Zj%0O#XQ$_P?=5$J_hHWu@kjGq$=fV<9#_2D%00q+CfByux$ab_Di;xFiMip& zx+^_li(-$Imj>u|`?OiXNP%gil2KG|7wr%UgdX_+_HXfh@_h`3eWP+RGhU>hP9L0B zA|)n0Gg?6Z9Cc>>`ti@ylqwE@WsU}Qi>K}UNY6$i1+Te?Q8{T$oelj8TsKJ!j?CZR7S>T`u< zvR5i66H00T1E5d2h$&X?3{7QUq~MY*KB_yS9DJHBLC1xbH1KDLU_h>M8BDFWHLWo zdFc4)EbV!v`n=7&TV1QYlW>fm<37dxm3wvWwEIIwoOWQ>Pn>at!FS&Z>U8?9rcN%&%@W{8gjBbTDSNT*fZ7|a6s~FHH9MjI+;!t*bW6kxt_j(Z6MCk#g*Gsg zqaDL50#5=`@J6T&3^7e34FVgpZl(`PE1UM0e~(Z`N+F3Ek6!gpsUEb+eZC=Ag}#ce z%bJ${ICNON>LzM&)@#gX9<%Son+|kRbZ_)NavH8S-i7Wt-g~b8?zS;QWA4SZiCg1M zb9Cp=irAUs`9JG?C``83nZr>1ucO*%EZ2d`(L>=^(ILhuxKj>Vh0Q^5|Lg~ku9&xt zuEuZXely;@sNd99Aa{2KUix*>W08{)XLwR@iGQ+>W((Q9Ge$>`i0}D>bis!qZ*OLd z(XUFId9Qs3v~#%(J+r1>(AnM7R*AP-M4QnLL{WM;`?^ewiu2V^Y60h9cQ4N)ceZPt zyQg<$%;T6u*I8-4Fi1*p-u1Y>?Ue1@LRd8)aSeqB=(2g&=IJ@DPh`Jwkya!x?G8qD zy#UNQEzS1EIpY(eShHyzWQW(G2C<|$Mmu7ZM&E7CxEq-r>K9xUx)N*^5(B+*PG|1& z*WxYVgVi#<`>Q=^v!WA7rcl9dXl@JqlWeE7inMf$i4nzBW&w1!EXvj)9UL3HuQ3|T zlzK`j?gy?rZoz$14R{-P6TK%q_0-as7qjIkx^mWa7k5U*O6+oTIr?62lv^ZavC3)SLnJt3B3CW@E#XnuGwzSF{(uRgcgUp zgu~Ii=nzhQ+cO`gpNPCdw~;h$Qc}~TT3NeIPB=lz8NWyNXGzH=v+A0MT_@bL*cV|o zT*4?vYRK!IM_n)6Tjfg96vqhlru!fFudY&V!Sk0pmuE1D-r{mU(IxkArs37iSMqS> z?Vs%5*$2WlDMlK{SG8;ERB^l*M82jX zwT4_p9U-ctPQ0(2iHOu#m=b$6`-O7P(h}K=wc#Bhvoee{@gqzlEgwufbywNPI;XLW zWs$jwCB;fuOIXrO$3bXotS`wew^{((HvNgH|Dwo#6 zsmRMfTmK+e4d+C6M}=W5T0PI#_XRTE`aEg}dbdXmyY8wmNg%&*{dT2l91_uh2cyxUYomjQ-! zGI5CE;XQtsPow8Tad<_rX~l6*c9oX2^@;(!+a*|43((aWH`Nx^nVZy`sChj|E`meM zeEJlwYxk*1Q1)|@Q79ng7k-O=4OI;2eFeQU9fWT@BKAhjnv?ROO12!hOdKKvffQ!@ z>pK@^mGR$ZhFae1cM3XR{>VnHEc-xroo@$no1MSM3}7o7{;>A5wKcsl>@j>W8*R@_ zM|EjzHrLE#G&ANFhN|2u<}oTL>o9BiFJO~qk)!3!k{7JYuh5V^Aq$}H<)d;E+2QBJ z5x7sDM2&m})RE4EdstuOdu!3Vnc;K=@;%h`gT#JfP;902lKY7D!!A^v-S#x{*gP8| zRp}*~Ib{FV?DKT@-e_Mq{5=*QMap}6Wsb8S5!M>Mn|l!1!TG^CVjNTrwYl!53g&bA z=G1<&ly0PXvn9h+NnguoG*2+qGzay+%>F)U*;sWiHyR}@Ell^@5sj>g%qT0(8-kr!%k5C zmNn^5DJ)(SFGov7riJeK`}$wHKfC7#_rgK5zA!nb&ga?Ab-`^=&Ku-`(N5k!az^B= z3SVGKoAR))qB()|P*@I<-MD)C$#Bmu3bpZ8`XAi|bG+p=)S=Jd_@2+a&+vsG#P8P~ zH(WAi=DO7C1~Hw;BlJQ}(N{L)<-0L|s)Lo9+7oIsw;YUal3WZv)d{scv6XH{yQv_- zgIU=QES~&iTU`5`A|Ha%)tdMR?zIc#XVNISnyg9_#I$JXaOdC{kWT$R#q%Y2Njt9Y z5rmwb-zs_bgwnKM)cInJSl@Tskzg+pn#7JZG^3wJd-_*L-XU5PVL9GlXrymIogyuC zJ)Of;!qmrbQWw2QF3K9?IB7x*5iUhNZg8 zTv5ix=0KN}t*fp(&y$P=N`$lIKz2L4nC{T0P?^mm-%^DxP2Z!+(}lsJnGWB#CDbDL z%asHLDu7z;6iufVS1YQum2q-Qd95UgMTHuXk&%w!Q6Vza5{^v$l`irpv50rGJ;8fD zxJ??O)RqTDI|lB#YkJOwT4^g#dD%s_g;FDY~VAY$R)^p!b4&)3GI>i!AQ5?Y%o)IzLJEw#nagv5g_lcV)h)=K-OqT+s>2}O7z z_k+5sQiu-p5AOz*I-k-dU~?(Hxq5a)!>d1rZx1qUg&$aF1H`XziJl1Drx zPbK52W*|S!g(6@)NiogA@2kaGxvK0}wjsYncT?Ac@5N4Izi^LrA9a88yK{N9>?*|T zm-8|o;{RdlQ9GzG{fM2%iA)a$D=qY>ndCCM05g;xPhWzE?Lc^LorGTjd~!i}=?e1L zWY9JK(sbYptWy>!t)XigCYO+gqwe}b)F3PkcMBJcew6=FdP@bvzj&_&o`w8^Rh|Q< zkbB`?f&JcF!P@dwuv@N)EhD2NeTC!Fd9@V$CF@Y*G(!DNdrTh0*zoKsIHIZa1UA5F z>;+cDesl*5@D!WD_2+5M!%ky1GA6Dhx0T(+US|8S9`-Ta8kOV&pm5y79HnQ|!|3l+ z6Uqsl%vm^!eukQ7FR6kvR2^=<<>0N`9IUtFT1WLb*5Vpqh$JibWvjePY$n;@3$q9W z)2-2SLYDlitO`R=WxF(~;Yd)t^2bQNYf=2LM_$GMmW^c1>0bBA5UyYs7YSt8-0wPNRNe{ zA_`jWR(dBjhB^v5`A6tlYEtLmoLC&%uV<(kEJ)JCdpH5Mh2vja#K$_S>(sw;og&q3 z3ab=R3d@V7wQ@JbsSH#WfS$Ql{0DcowWUo`j?`9)2t|d9aFHJf7rGJB8POu{7CT9` z!F&89w^TT#Jbb=&P-v-895vEL!xy10lwdx@bY!?VOoz7Jk0Xu8R-39ytp??>Eqotp z)BC9-cs`pR&-~83r*F~y=pkSk$eSPV`ciD;avtc23Y1P||9U_#H7|B~Cux23A6 z^l?Ztq&2_+mejV6zCLG1npsib^epX|#pY6d0i~-%q2sTG6 zRKP{SzvRiLWDC$jFCy~iC#_U#T+4T-R-n}vsCS49D`5GS!MN2xuCfqa0N*RnG9_a^ zKcH4(C0PO`U|H;$A571K@YZYt?kEclTng2Q23a5mb__ zN*2XqM{*$iM?0a#J>h%b3ChpmWNXl6n#13`CfrnhB@2=S9G^ddZuuH2NSvABMm!GG zm*$A>mjz=)M`VFKcnvf3I=H2Wz|WkFb-X_~Z+)=8y}{RS2=ZfVkRR({tEV-=Ov|O( z_r*2}XZ7EI&bzH>^&ZTC%h2*az}H(SCUduUU_5?BzWP1bjNkB>fuBC(=co9@YtU!D z!2f)~PjB$o7hpJvnu^gan9E8Ar=}?MwpBsjEQV*P$T-vnzoa^-Ib|{9^58f1@tfip zonpAgD+C2;GPX+K({MNk6eK=?0BOSClcBED!}s$w^p64X%NZgaw8~5Hnt!T2LmM8U z#s6ae&ub^q1J}?G7x3&9_U1H5qUXV(xq;s!f`G3ZU}iqS{~pKd?_w{%Vm3a+e_X}$ z%%6Ms0Hc??Pr2+#2ln_bm}L$qd?VO1D;PMRuxD2AB$IF*RPkrqQ}LJ=dpQSVQwwd4 z#bZyfUjD-9vzY1mv8P3`hc!UKw1cnt0efQvkNsb?^B=V74L_xgwR4gKG6<-4b+bq zOE31~8OWMHuupfee-wK5!_U3cqxBY$JQFZ4Ma-6=L^X0ExJ!GrOQ^Z}fl(`feGs6) zzCc{WCuigRK7hVgnDi34WS3Rof$fBgYXZjQC3;$f9)CM#{7X<=omzcdcSW#&e$0q} z@ac1~rY*xbO~xnc!8Uq9>_?qm5%i@FT$^v`w|}AAiAT%sgS(oJcl4qMRCPR&NZE;o zIA&Xd6IB_soq*Py_z&+}1{Kj8uu67=I&~b8mrO$Imtki9jk9b9e00}>B)1Q2s&AlF z)yJGl!!_P#6|7lQM2-L->==5ZHa?{-oYdPv^_lBOb{`p$O(0Um6S+B`O1V{%Ag6B9 z{DhgTLFN#(o?WIk{n)4N`sgD?| z`dEW+4hC_~+*RiJpBdkYdz)%!{V;sw=@YOL*o`!uuwL<`J;x>_go zFhS5B(gLmXKcKL#2KTQowEFStHpu`6DR9V8U+6&WbE#IS0=FZ++(1iL1-UiW;j0+` z>8gk$x(}`>UaM`C40V^bl*-SnB&H(I$f#$a&p$xkMD6)lVuP|@9!c~ecY~|^MxBjJ z(;&>Y{`7M4m|C0M0H@=P>T&V~xKDSqdK5*(#F$;we?bQx;YDiAlOkxwXggMYQ1H`UM<4eL6pp; z99*Z~DmS$JbU(EaF<;xR3`E4F95GORAiYznpysy=J52p7u<9SEcDyzCs^*5Z6@eEbrFpk`I|{Vj!x~T@ZV1&d=5^2SA~uvxK6wge#N~ z^bWm46R5hnKh^c9DH6!FY)xf`b_g{TuemHqh&tfrTVI+E9?%>mzzeh&{?A?H1CfD@ zNY|9kl8ebf^aky!=p+?(CAis|X5qu~L0l2_pvUog zc6l^G8LBG)J(5GcVEm|#51yrJ8#ggSK*eaHBuVigvA$NW7~dH7hLge-&DH5A-usf3 zzs3*Z&O~1d_o)(059C`NWJ2_r!t2wR{gFL#MX+5vi3POrw%PRPNDrArj;5 z@>!$UQkJcJb>^;oMaooP&!^9x+FD+HhClwMFsI8g$aC7+%?E$O2 zMSg29HI7Uset?m&1!wKPph-T38`59YTyi3~ktexrMS zPEe8wk^RosrU%H!qV=Hrsz!gpRamB$Cb^>L3Ca!J zb7vy5c1xNJGJHQdM^I7mdP6)8UH?rY9#!|0q<|gU5A_94p`iK%&#WsT2P~t#^c(gC zTZ>QNzjHRe3RjfhsXJ@njKz%!`dQ3Q`X4S`A7h$rjx+wnKF0OP2PU^Rw=|PQCTm_r zR@}H^=at$b9hJhxqJ@Mak!HbT{>{GS-hJLdAeem%!Y;(u)YH}T-re1^B;bg2j3k6o zLVtt`h3ATC@;zmax&r#TqiQ^NN`FSbl-tQnW3TJ(o6gy;#Ll((%?(Xm%rk9G6PhII z;{LJ~f`8>?(;M?{%Qy3TQ(I$OU01F=*OMK{%wt;7-HD^9;7L`hf{wpmsSb)!=V+B^ zLS!i%#CwK61V{N>`F429x;wkexSM!U*NHMG{hyA@74qi{Bh)&4*&f2vYgX^#7~{PGBlX#Pm|aKW%Od;%48 zBZ4FR+dVg3XPkrGE8Jek3VTIg?dU&YTdbG3CoU$IaEHm8V zG7%qXz}BYkqlWG?s&ksD59B1NsB~X^ETy5w^dIqjv~cu&xKe0-XlHPneKaT5E{jYpT z-^w(^T)|?5>&Zp)cH5ZvgNa8I_s74t-Z%C%&Nlaq=^0brw$q$wSkB%=M&c}8pIJw> zM$DLlzGkcxh7xlI2uHsOIl?rl0s8GhI3uvvKfwFld&axX`@5&0tB2!K_K?h@*>eK( zqY*h0nC$BB&g--VOGk?k$=n(!+b)YN93U4`e<9*BKridJ=6JJZO4tzGzu%A}xK)1q^#QxUeyoz=C8EeJ_UlVWeE|{&{1Ic^^!X)?ZEMSEqxM0;#g3GeBw&!kO!(ev^;>ki*%ovs_9JQ^7c+YL8I?%c)Z%g%DNWojPJp^~26+#D zdU=#Jh)^oZYDEA;wv}8|92=b+xfSm2FX4IND3&ulyL{HUjDqgGf&=uYJfR%VDwI^r zik4LFaHWmi`F!+5@)z!+K1JV~AIH7X4>N@Hsjs%l}MifnZF3ToSs7Yaeq>t2r93o?c!63Q$8vm)Z%0pL`rJoUL&OK2TwW~RO7Ge zF{y_zE&RLhhD)D4Bl|*jTy~|5ZqBnp8tEhY1_pX)?^Iv6@OgEJ?zm|$w}YC;YWn$x zJGwQxU-j1w9e9gzkGY|>g!LEWJi~6jGTV(`ZoCNR#@`Gp`7A^hk5TJ!wY-)*hC5bJ zx1p2W1MWgQ>9D}ayOcLXCSr`wDGNz~Xnsg-ttH{UX0*~w92%__tRAT1-k5zOt4C&s zteHP1d#cL<^M$D3{}q~(?qNrCGO^#V(D00JNZ01en5%$wlMh*eSVI*~(6u#ovfj70 zHx$>;U{|sTtm*BR5vJoh0~dkfu^Y94Xp4Hi3*h)|QzuYvoV_lo)s<&*oV*eBfEy8g z(x`ce#|$8G?V?6OBJD4xM6QKY_;NLKe#n}e)%tt+AJ)v>VKe=W&6FbGYK{wRgIoPA zX1A%9zB1!8fUX0w-*tYteh-%ibwys?V$(%yd3Z{X;|h>RX)70R++ZA|tIsDe@2KHq zI-+8|wc^TKDOt?{jl3qchnhp|(#}g&)q6yH@(ewdp326vA@UV?&T(2ZWmU9fWJln$ zKij+6*~DHq^F&6S@B4q;2^iEhtV1J$j_}mrQL(x@o4TssrqAGO>b{x!Syx&01}EyC z-yl-Im^p7)Y$<5XGOT9Wv4^Nf>sbNX%0W%2;7r@I-+2Ebz>*-_FX+ZkVz5`=!hyp~^%e z(?mQP>=XSIohg0NZqq26)jeYxnQmLJ*-~t!3^9i0`m@v!dK#Cd=WMCQCAv+#l|GLO zy!pEG@ZH=;^&sysqYxXcLk-egYL>W4dZFZ~^Qpg4^}7Xr09Vx=B>V*E8`N^TGt(B7 z=4*(MypeZ?)j%K5G3YufXWh+eo$)wh`S&&6rt)OE7F9j8EIJcDrHv39b#bFXZJJ?t zWLgv3!)i7+(x>WjcnaFRW31Cq1 z{iCLp61^d9JnEWN@UN&_}vK z-~S9x&5vM^AD4Rww%|KoqIZS!Sr= zP*YB`ZnPD*ZMVFEPu4od1>U|u&qf;*<9~*?x=#9*+(FYmLr?xR9Z#gv2ISGdvg27B za~OU;-<7pWE5)krLKHkl%~1mCPBNXki#Xy3%$_3{&7R*Vr_PdyCjk2g-~J@d#~YhtW&PgTyyvXB==j+>9*7Q>;upu^Kw^ z%OF~(<9_;~{6;P;-i{0j4e^ceO?O6fW@Tl4fAVe1587vyrc>wOjQL2NqBcb&q%Q6P z%Aw+92Y zm_7_geGJL)9r#o^!pSHmG&$z_W&|273QQsWe@XZ|0 zci?pbwJkHBegQ$LF6LJal8&>J(lmm1yLN-d}q#5#J zR9C-1#9u^w{Eo6tc10J3M*Fiok6az?d^VfeDPzTtOxF$nW1$rYf5nNSY9jH39I7p# z+7YF-Y4j(iGw3t@O|31jjU|k;btm~Lx|!TpZXwrze`qLUENn@!ZHEK-Hp5ii9lj)Y zkv&h%K!o`}?J}VtFH{6M(QT+wo~7(ZY(CcyeJY4<59KDxQt(z>VsfNx=q&ic7hKis z8?z^5Z2z&up3l8IQZ4F;oR!1EP1L@eR(2BIQNv_ZClX(|mwZv<9m80o-B?@>`K$a_{d(gb(-Vu=Ryw9J_*M%HE%hc*%9ySKDy@IQR<3M)NoNxAZZinxWK$Xb&Xf6K zF;XJ@_{NK8l$Yul@-1DAiE>-`&bl(-@uY(7^^k9*KdF0R_!Zgw0+!e2j+RU2f#$pB zPsY-QOMGE=Ia8VbjlM!Zre-0Ncb^(hXCUv;9j-eszzmqBUQ;e9+m#ET1(MQhAsDrX zUx5TddN+E`cxL<9U}9iYuwuAd^oSrur$*aEuff&X8tx3Ttq^$`-74;oM}VH5ME*&A zhX3eJ`Z2SBtqyumI=@~&5)OJ^!$Q+a^HTFH(+cB#Fmo;XmAcJP3~%Qzz+3e{IOxe- zX?`BJj_ttAr|pQ`)h8dpd#8q0O5HYS0bl45!a$HXk-8RHz|c6j?1G<`NsF#dq|-!c6=oxq>qcC+)K>@NUL)NHap z^6FR%6o@uUY9&K&} z%4ZY!51%kGAgR1Hd_ymiy58`vt-+=-$uy4|?n>Hw#V?-+_d7{^inCW2FrsQg#BwQE zJ223H(09Z;-8;im+0)N6$7A$f@jmidL5pe=+7P}INfzS89dPl3z8^G(`-B-8sTIs( z_8J%Em+3tE#fAySq97dAGWRvFFh2m5e5|FNg|s{||7~t=&Nh`bZ8v^|`(O`{Xa3?R zaQ)cd7=hYI)&iY>rBYnh!0u=(%#SXMdi4?mjdMJK3g)zkVSHBMUGI_b`{lZJ?uF zj?@D^`eLXV^6JsRvcO^gV1LAy*H^+f-fQp`@>THF^Y8ZG4tRqvLcJrCqGJSJlB7{e z7tkWy@cU4xBlHe<@}A;o-ErM4{ZWGr{-Ebgf@!{aqxnCx%e>2c5ghj~rqd?B@evqv z%M1k#pW(CG3+|is*Z{6e=Hm=EPcfyKz+zwR2O!geHSo{m|DWE+GTQ>-hi@n!jxvp zG4{fhLqEe~y-nXjH<+KqjloQ!X%$}3LqK2MC0COk3$+C$G6fFGp3w1-0sO?8!BW8+ zfeC@mU}Y7DLvHQB!@#$|uHb@Dop94g+2}xFsMufX3ku72^)0Tp?vod&UC_JDV+V4H zd@86R3g!JgSNbN|wup@LZcbQ+MtD*m@A7%ItN8LSWbi0{Kn>v|{rl@fh z+T%2|!||U2-svh-GhAV}Gn46NR0Z-4@+Z}l4~VH86|P5h(ImJVXMkpw99kLN5j2D6 zbQF%a>%fUi3R1z%K{YPl&K-lw6y!cH!SR=pk5jO`1C-${7O z=GW`NSbJw^iM6Sk$p9~5vngbhjf;)H8u!4l^{PIk`&HMIPvaJ{BXPc|OZ|uHtJ$hu zPM1oE8-*j$Es>nC3F`KGp|`>Q!Er%*;A`L-+L9hP7$^;Qgv($}>ELsF2?WC9!Ul1g zbPyU4r}_gv(L9w-J)*BON7%8r4$$k~>U!%x6 zGY)(%JECIcIXe@eH&ctjhe?MANo6@8T1BHUIyx}270k88q0XTx!DYenVAXNK`2jfq zDk55Q6I`wXp(o*?k%`gf!a%WtG!CwCv(%$dqP!)SQXAm}T@z=yY>d`MU0MAueQ(1f z!!Quv-u!f09&b{N&%hANkE6?ESO}_AK9DX;aFt+vn1I>*3c0kRsw(f5dSdo0LhpES zPQ4Q<7TO%_8Z3y}lM(oY+8lQv1%0(9SRiy7Ug3ix@li^!3zTF8XD9+4eIKGX+*W_3 zJ1`_`=F+)EV1ya;VSQ=C5ko2%Z`sDr#&S5$-{Sl>*jUhb96ZDW`Y1GU1Np98d$u+c zM_Z^1#5p*NlvdKEt>QspXOxSY;8XiAs9Q;)*};{;!odo`P=E^3V61Hj-UyZh&weNT z%qv7a(So7{e&7b>l%ms$!(X_9>Pr|J7~UJo8~fr6yVbbb zcnpNMYQ}5$t-(-De@ExzQ?M3RWg9aw=o@ct_8a-48gdQEClnWkMQ2BTV5~x+z9BAj zF}OZBD7Xo}zWsuygJ(hYUl}rjwbeV)0(ASY!e#M|^ikH7SE?VCdOFzTx* z@(zBXE>(YApJ143xM(n9?)EjN8SA4h2jE8>((k~k(^B`Czsec04w{$`P@ug;YmTb( zl{s=9sgF2C@J79nKfpK~6{f?>Llr|`gQ=lY!Q!FxU@gR&Z-vsro5JmJWkkY@_M})w zsw`K5LwXwW$Au9AjiJxbE8#HfVh#KZzK?Fbt^@d9@8K;SYnWvyYdB+QW=Jty*H6^f zgNJk>T|NE-bZFJsK}=sna;}oe-~#D2O8q4B@&#x<`v|wA6;a_(Kk^{FELv^5nqlk#((54bHljioX8Gj zN%kn5mW4>`)t31Lf?F^0(3v zT(Af77kL*{Mh)fCa(Ousitz{12Z@o3$i<;~o-X_4Vu)t0Q{E`8pf*F@A=Cn&ppkzF zXP<85VPwneK@KwR9vqezz)f_ux=`H)@8a$1eszaB2l2c0@cDWNf6sziHDu}+ zfH?LBZd0}3x;73Bs}oQ`d?(6-$WWc^3uR^>5;S$vimGWFGGO`PKbk;3fg<8RP=(%N z)8GJn4NR}uP-^GuT+C3se1L!NDX3Ux!Lz9|+=v@#wJ^%RLH$?+v5I_J65<18@T`WG z2U@KBs8Fqr|L6>7&Pi}sx(pRct^z3&x&;#IA3dDS3{bhmL%*(vE~PwlH?^P~Y6Z{5 z`G|(Bhaze+G;__-$Bp6BS`mz~s?ZqK1%yC}av@k8*pwC|+G0Kj*=H7XwGHFkX`lZO}vLY))g_ z4%N#ZI5O{q66OdV&uRy>{ZI~_gZ}6&{^vEmK0%oj#xnvV<^?~l00>vL(c>MV4x0fD z-dfPnHb8T|5ahG1#4_kVRuUV3Zn<8>EAT(lp_LhoS9O5$t2)}3fL?*~C*JcO6itWG zYa6gF!+cxx(-V3rbf-(8S=<6$%09I0EdDE3UvwXRn}NNTaOaowb5HBwUHiaMbOKa1 z-M-TTv@3uh;{=Zi%qF;(ZixP|R;m{%puALr? zToyj#Bl_qawl`?wCn%pjVrG1XCMN?KO#9CskD?t>Y`J=|AVz_JMv8?x%7pjJi`M0Z z$|=_$Ip+U-d*d($DHxX`*m6gtEY{dk*h*uZbMD2$VE_WWC$KNlb-T(g7a^D{HGnSaN^au@9X*L zU>!m)MDUgSnEU$(bW-u?rOt?l*GCTyz~Zdxf5-Oi=h%Pw*{c6-=jWg8{)FG;j_eQ2#~*0r z54@Z6=jPY(K@3>!9@)u+Ok8rEAm7*uRMX$&#fM&#)9S8 z8{WFv)W38B>!RGyZyzG{d=meI*~|FhJ2?w3mOJ5$d<0SQ$#h;aq#aV*$f8 z`Mm~uuFdKYr7-Ghs;b?!Ip6_%q&vv!-_<%p`S4Nss(h6nL(^MQX{6ME<4Ip_FEkV< z!Gh(UQ@L%2xyQu?Tg>LsPC(iZ+jC6uztXoXgHD|O+$Sx$SeYG7^_*91^Fw}4l? zM#}_i^M$et*A|D6Ve6`{P(MLYy9Ub7G^~Mx5Rp9rC24c$uNy+QdI(2EN31tIR{Bji zmXBlYy^6X26sv}SWA+o)yt7(J9iSo3s=;RxIhXY~I!58h>4hWXE}RYHuPR@-nLPbg*d57Fk-2fKO zSf(Y@nY)TO;X2%7zA?pEcAKwR$5}^M-N++OvDUU`S>{+A<~XwhbcDL7qOn6$VrGld zf1@U*iyEUW0p*wx!r?OEMnNOI?XDosw93P{JGiZ`fv$tD9?oX&Gp=Utf4w!mHThk_+1gwGvi^YxN-49~ z_}q8|_u{Mdsrs}0E^a#e6}h z!Z~{9B>NVp*OBIQxNCc8|B}GbP~PZrv6WhrEW>!XVmg~S$klo8GmbxM0O znQ?M?3}Mp>Ywy_j_-3fRpO;ua>2cDtgvE&m;|9bIjeTnUfxNTbRLS(nSiu-FT)~~C zL$_9E;xBOb5kuNVE?3XW1wi1d9*zkn`xbi}x%WC>J6GAu*>iHHWr%rXX8OKf}D-;*9GTmzH46(>3vRayY4NQclvF zxEcv<;_SA)F&-#BNXtv(4%0uz_sGlFg)7^0-3d1Ps zrMfOTI@xXZ|FQ>X@6Udkbu4Ro_VMh!IR|s%>}MR!T!-EHeMh}TLXE?P#iD9gqAIhA zt7y1wPKl`=w=AJvV$D3K^R&yWMvTcy6xwt=@uBC*j~ltBveUWk<=#d#JnZ)Zpi1% zQzy@zgc|X~V{gW^wb{&V&3g<*jUSD}q2LJWI_sQpI-vPl?5{LU{fa8{#*!oQNBFY; zFW(x^EY}<7Y{xA7ct=V5NJmM>IrM;RUuhrinC0x__{$k`6nAZMkM#ZND;8`MNsFFP z3WHba(*JIJU>O|WJmE-koxEJ$()pL?>y)=fenZlPq{a#7W46X#vVK6+^EY^DoHy=( z|AJRvU6-Wa$G-*K1_Sbf+qm})>zmGZ8GpGLjV_vlmMUouLchp*de%lgN zf9I>8cV=>8(v-Lbae{4~wY}x2sgkj(akt?wLj&V;eOdUl9N>S~?Pbr>y{NV7Mda}E zL{kbbPdTv3u+noaI~> zJO#WnefxbI{4M>d@SB|AY~e9_9@_tPuE+eT=IiH83Rt3Lh5FhpHr2Srt!2`qH}8+|M{m=VLVTz5H3Y7Mkb_d3HIk<7nw-pW)1O z{^J?sj}3ke#|r(T+r^z?G<+u7)IY;F*E!q1+gUZUe#UQ^#eOt%Z1A-A9Ey&K8tEAQ zRm+Qn_VK;*KFl{RrB6Y7zOs2@6L-a~jagw?i@0uQb`JZL^>AIGFgjxSZk`+SCH_E6 z^Y}gS{cR_$LBoBvEttfrkQ`d*O>$p$)O0+ydmO!-I?p73pWxl-4snk(5i0OG@PHBU}XN>517do+GuQpv=q(PB30YO_1|Wn@vRB0Za$ z!j~`{GHtUdv1Jm}q$YVE=P@SbN%UAtTF)A4Q46$k(TTy8elOOOr;Z7ZrEv3F5RgN= zq6_6GS~lsWIO;38S}UwJkbU9u0mVl{T;_8PKa|@-LZYF)s2$=8GW9Tm?fZdIVcykQkSWZTBMl0u}kyhBqrsZnD0U2 zg`{6&ZkuhUE9_ySqjW692Qz&iJXhU<>!4?!KL&ZOFAu+U%{u=rGpA?H9cKvF5Dkd_%yYVorJAW|OpEwa#vK;UoTl$) zn#a{a#ea9|Pk0#o#T;h)^IObM&A-J3;t$2=Lh2el+rJ;)-~tjn;DjHes(E$89vxYh%(d-)=gj3 z(fW2KZ*0`|EU{72+k}?+<|8msHQ_I71#54^RkjfQMP4JN2&2Po!(~FvL*~fg$bVu! zxvDxB*ZBG9;naRgCZ!j*1O)l zL2WALQ+hHrw888O-7U^#xWd)M6=H-g#E+owP+gd8lI7OxzH#r69UEa?8@D8`Q&OI! zDTx_L@d=&cLNRB}vrVr!9W$CtRAOXr^kw8;G#q{yIU(E^j)T9P7x%ieh?j_?-X@zs zJ5W>kCdG-nQ6sy_-`4lQGuc(c-O)b4o}HcXLvZ$VPj#&D@h&w`ONEyy(Lt@ONxCxB z2mZcpJJpddj0*g9+#t$C)u4Ma3;26{F@xKb&w4ISi7APd{Cj+Xy#3>YF?ZwrCX0En zz8&44X{D`^ugE04Is1v{g)P!j*(GmQ3nDVJ0$$|lsE7K6SdQo(DkX&>LgA<>)G3q_ zXyn`OTkiSQ^~m|oJ|$akpX{pdTH}1`iSuj?dr)D~R7t@7b48{YUzgd#jb`RkqnToe z;{D6yBjcGQZY4aS+M8Ngr`U!iJV_{(;7HOE+r&SOPmE~it05peNj2}+Z`X3zn~6gb%O(s-8jy4_Hr=+!(gWU0SsYIZmoKed9yOr?kN3u-HGp@udI)#+tWYrh_Onl90=;ilmo!GC-e+#8%V?3XjoWNmYn z&#LCC=$PYL81;rbN@?nN^(qLLBdMLbDX6xW7*2CgN9o@P5J#B7Tmjuda}l#-8=fG= z?M|wk^he@=#Dno3+ZgL)QyOaOM>6H%X~5&oYZ);R_rm`YW609v9_l>u#Jz~t+8*^B z+{YG4e@iz}=_=z{vaG;&e4M;X+7lfJAKmZ1ioQ?oAvt>c?2Iqp2$z`U&(XVgJIV@^ zqerE%T7-DOT%qbQzj7t%*HD9O(ZbXph(Qfwv*>P|#gwiO#oUX#6ni93p2U?&&66DQ zQ{w&B1{T6N7*w#;h=}(kU2rn+5d?LJ>_=9o{-mms9mr9r*CN&aN}Bvhx+gss=SeqZ z7I&m`QNMd0cc@#WUD7IWugV0Qd;58&JFaGb$=>^|NKU%FRgTRQc6fZlrGKOOWmNwu z7a12^3Gy*Z$yTV(3aR;thV(5$qLxFa(cN?mw6Z4&1rs|a#wHF-T9sf(SQ=xtOf}mL zSNU`=_wOwv^MtHn1To@%+P~U*z-$*-4 zxXI(%MlDS(f%;*N8cL02mh1cJ7>mm~Ii`Bzu(*o}&yva{?2IoScgxn)8Zn(SJkq7{ z_ql!SJ|>a*h55vkrL$1^-JIHs+K4x5b2UNvB;J?cG9vvWos`m)DyV>6s*WPA5Mg*i zJV$JPka#d$B{ayN;i=#rs88KW7N;lEdfh#~m#K<%q?L-F0T1U>@sYUp30Gqko6cI*{M}UEFiKwnPIFzj zzu8vY52gZpiw2K?IzW!XvC~FdsWimV(L(AkPmmP&-2ba&A;R4e>OBD!zGuMLyCn4# zDuh1;mj#}BL*4=I1+KQP0roUU5pSwzm}i4;n(vqB-sr>dJ)ymP2)P%NJXj_WzuzX; zRbC*{mqPDj8q<>gp7DyljV;dFH|BZV3)|_~QL+7Fr`uwz+s&-0j@f9urW>no$@kU$ z;772hkSDvuoT0~J4c$#V0n_m<__SCt`VRD^r@c)1K{Quh(- zFASB}N%AcF2}F$UhE2vC%Ll8;a^CjT)+=UwOqwMR6p_c^b@jxs4cWB(x_i1Ad@ZmH zE3t*xstiR}piH2j=Ytph5!I)-knhf;zE=()Z!lZEf>G~oJu$5_CK)E1-s#Wl&*-}GNAyMb zw%lFT#M+tV^cK{`M-ZQ0s+o{SPtaC^x@Uk|D_!Lg$=Rv?tNaO&^;ZN#Y^M3t)eM$W~{X=B;KkB~eydV<2)n#!`P)f#f zx4607F_va)GN-A5$lI18AERb;3UX&nafEIp8bGxp6FoJ9HdTGEanNDKYg?s6t&#E> zdB1%4)H-TDaj?7;jJ|f#4dev3M9QI-sEsgIdJ{b)E|V`rHp%;>BEo2KuJlUcOn&IgF!NxN8**}nde$2dL*T8#^;kGkd*vecs zeI9YEr!+XvluR$Dhk`*;mMTHDKt25nL_S;72}GJU6;3}!RKN}Z{p*Y}fM`gDlsV)D zFozltYr%Q#uC67XDhSh}VtyrjuRa5Ic?1m^xHtrUJONJ(<1qb)qEHd<}@g)L2x?{!V?-CV?681l})$$%xXA zTB|kI+~j6;3UNSlsDC3*Ra9+4JO*!~vwA~&rF>Dw5g(-U+HkqPGDq?5YP5MPWt__j?gCC?r?xFpz90YUef%+HnTT$(q8X!#gZYB34pRonH z^it$pX1m&*o=PQ>D)mk~&dx%ntvu6&${?4KgK>_SK`Sc!J)XRYtaN?)A$+!ma6{D*YE7yR*!Vi_w(_2St(H_y(EH?kL>yTK?vJ;L6y*|8 zQr!lo_AB{}OEpoP8RM`jKZ*BE})(wm(S=p*qBCksii5=>Ha8y_( zCQ>!Xs?tj`g^HD%(MLgrm_@HticlbtX*;QRN`BlWl$W!i7^@9MTot%|7aUzSNo3h*ru}`0Z>VNyKa4QUMS=_ffsnM38f!rX~+4Ibelf292vD z)eO4SKdEbSBebMD;_Gg?0<~C4)@~8km3E{_`Hi?tP6RRfJXm6Nk^hMzr&yh8q_%)V z?IiM`HbNnoLSQgL0HuMmJPu5k(O{-%Zq4htp4yy=jbV znJd&5)rh+1HslcX3jC6`t4<1z8;F3sB)b!*mDTiiRF+qzA1f13_j^=+Po`-VwKOt8 zWeGpARV_qpS1%L2)CpQ1%A^b;2NH`F5M<;7WM^U&_<*gIwy3~fBHyKk;Y1JuL zVZTT;6|ZejSCC_ryW~k_3wZ+h!JWvOj3dh_Dmg(tMEVrH_6NCH*+3Nr{m)4~Qm$*~ z$*ZU$zp1?=x+s~{AzWkhVfI3e-Wb;j9`zY{NBOF~hrd%5Z32EbKpRK=s@);0s*B*u zlnk}^ajgO4mOl}%s0ON;SVygwt1)rfGo=-sqbbUET2ENWY1%rXJT*zprDhm#X1}E3 zs8@@C?Kezn#8~AY#A|Xo)|ouaMY)q&p9zWY$g{*Wm4NCfQ<_T~l$p>;1eBMkwa=pr zhp$vu`4B3bKWkOUQK0Rbs3BSdd5O5Ib?mT5GCmOaw%#*^Z+6}coia_R+S8(lHxq-FYnVO)y`T2`mws2 zm`l`0wqiK(JFx+C?-(%=dAnZJMR|gDf-VTxiKA=>p+5DLIxRN_>o^q^5Y;H7l1(j8 zN^9?^8uCK2EM-wwQJ>Ub$o*s z4Zu0%CE-H#e`~cFeO?=YetihuRW|jPavk*T%g9AFB;RWD)pJaa)PfjKmyt`5SmV^e z)OWc7nT4$UE;64oiV7i@J_bbalPawiq6^92>CUJan@)Fw(n6$KAcu8Rn}Jzzg-VmY z5F~R#YWzPXodsAF>)XbsXOmq(6hx5{yN@0{wjSeex5utyiyphX6+2Mu?(XjHE>w_i zHfN{58^3EWPDNgC&wXn<$U6JdOBF$ zr!OYEX*Q~X8o)dSo8T6ktaKut)6?Zsj8oiz`a<9p0x;O;RPacz&blZaMQxW~T9^z~FEIS(FIO|>4%KWuxkf^mTGLN8{tcnF_?u81HXOiTrhV9$#i3tDN?f%@t-CMo8Ph zplGB&_T4vbRFAnY+M|5&niqVDKeS$?U7bsgBX&s|4__6c4QI#bvz#E*M${5{EWy$z zz3rjd>L@iO`!R7!Te7Coge<`k9wYD38j%^wePS(Bs0ZkU+A02^=RF?_wMv1OPJN>0 z`O0yvwa-M1`jA+`+*Ov5rYor5(j*r*(rGPU!hMUFG?%T-Mk;6H42wl!&y&L z({A5Z?30xf3_Ik8#6xnVT9zr$_R7g5&4o&*rHQ5n(oX3OHwMb6N^B!=9quZ-)OBpW zvYqNF4QF=f^Ob(&AdV2=AwsqHF0mbTHbg#FmBJd<5NV7ujbAIAV2YEhc3=6!=%~3g@_+>M0Nkt5HWui^ws3nT_59 zY=Ux@$R&0wE!iciOcz%J$Z5=GHAuQ_oTlwjYQO=)N-PIA^a%G#3@1c>i|aEV3U=64 zrXET8{x;l|i<1}CyYwWf4jI7g_KxHJCQm4`N~QI4mLc9#+F8DVXAHB2JRnTB zc*JTLH@<0uxUs?=-D;SvtkK@`wZ)h04`mQJPwykgXj9l}%4XjLQxbUfrAVG0shl;; z5xZ+`jIDhQ)i7oaIa8R($Ewq5r*MSmVQ%J{V0bML;%7^{G@6|zwcw}wp75;&A6H86 zuPmmQ>qj}kQ7s@)?&)1&{op&PAEO3nxAgz$w+c_p;m(V#n8~7#=*?F1t|IFi?4qu= zVqfa_z#zH8S5vC#-IYo_rLB;=vb|_499?!Qr3ucE=F+Tfr15kkp$^pxbRI{mq_Gn~+QNZB$3FaZZUBnRhxR_116E$Cw4ua=iq16e>7DD zt1HPeR366UvUF?Wi@JcKk$2pYeJo9bzI3m*m){4eDfL1BtlVbdo=mKD$N6>A4hsX= zO3*gusGF(&S^*dCOTuVS5tR4|Vh6UfJcw#74W`dZb*amk$Cu+qNioQkKO$6u7g1BW z3nPmk`5NeM$&71t=KJe8DAEYs3j=taQ8W()}x=uKQLmSB5P0y(7x1Rveeev zI6~G|vDc&}WHrj+OD&ijtgh4rwX^0Uzu;b<0_Vt+oAPV*JJpeR!Ey=+Um!%TmFlRi$hYtf&DPt|eW@A5N<<=$ zsL+gHt+OU|O3qhRJUt~+LF6c{J#_>ciVAe3x`V7n#Hw@XIpQmA2|Y;P1ajJ+RGR#n zN@2>Y8_74?KzbCOykoIOs%VwSj_hDnCP}g#l}P8QgW!Vs8Bapt>Q6jt^(O~Y<<-rY zlLr!0^eiQVypE@U)_PCsEjd-6&ZMd5*+1A~auhRDBZ-#uPtn74)BjP&QICn9AZ`4| zry-(cBkL>i@?kZcC{z~8!Q?E4*0)OS=&LB3Bg9F14Ae9|V2 z_i$5?bI@ISj@iX|wLGz$2*)$*OKlzTi%wSWV76mtyZM5ke*8>^E7{@|`WT#W1mc)Z zG9K!Qv<7U}#i*$!>Q4PRc|dJP-cZe0wRUKm$&I=B}quAUj+@rmd_$p=UvHxkBG49oAyy z=1_OGRQ#EZq)(01{~?}&hI4~lNC=t-p1RdRnYWStPy`B4t(d6!lv6E6e5VrilitLlS0>{pSx{T0zt5$uLd{2K%4#g+42qPIw-PEsw zSb6~Z&~u=_z16!w!`&77v%AQXe6H02d5$G&Q~o54r`w+7Ep;$-T}{-#@Km@-Z$h5b zH-U0cnRrD6X)VZV&|Hh82M$dKL1=2Gl4KvEud))JC6BbVIKNQ%^*W&RJP&8v$=V?t zO~uSO4cWfSiBoDz1F61m@R9D1sJJ4sw}Rnn)wRlZSM;XOcN;A86vjh)8hKGWGtT=0ze~ zcM%Z@m9LSkug6efOg-YK3Rifo9n`Nm>TW2JhCt=xfeLsz#nVU0y>ud#q+PUgyhoZ5J;I%YU+=nceoFs~m$ zzZrwlx(yZa22#R2C7C=$P6Xp`C^4Ee(#y%pP%xFpEcFbr9eP2Yyp7m@No_T`pZ=hy zKxbK#Ttf8JXK7j5G}PxfG7dB1sl-k73elcirCx`-{UnUTKZ)bS1w>ii5HhiV_@w#k z6`{{P1*PnD>Ic@zN|6`!Xf2pnuU{s{z&Exj83}*nca#VX)lxl~7zEdr2rV3OhIQ&w zmBC8l-=GfoBPMC4Zy`(lKJx0HA@l7gT#rW*jrFRa?yQ4mEC_1YUX%_(TpXeSt3iNU z3Xa2jjCDKUAM#djM|@PfL1Fe>{|>*Z@o*j8g;nD^!~-G%G~Z!i7!GNI0Agc z$NC)5&u%~=ma6vwxxXnf6}0kC`ak3dsGlE@VK{ap+$*1uMNygM#3tNvYatVyf`)A{ zva>6r)t1mcs9*u%nyc-#QnHy00Km_rsVOi_v2f<~-d% zM>>ktyqcs4W%NdnAS0pCt^u{%b^U)(xR)XhLIZah{U-wJB{TJ@(2x%VBXbZ@z2u5Kj`h6T)>n*(=RFgqa(3JpXGXitU4{!pYFk5d z_ZE&y0@Ug8AOrrX$3sW{7ZC~#+yu?%NMaU_xJv7$4;k0s`x5 zMA3$$2ei`5=%2NcAm=7w^e7L~L2Ha`&B=N2VVX%M5|xl8@do6IFpQhg$dPCOy`rve z);8k$b-e?QIUJc7&Cn_jqV$I%cOe3**s2%>T0yH_0#{uf<3bWt!xPX~^6{kJ6lIeJ z4=6X3-SZJ8`Wt(QAl9KS!oe|0gV*;{s7cc?f1Zx^UjrEn-7!7~kp}Sa&qLe1OgCdK zq&ED}CxT-&4}BaLr%PzXSID)bfUE_UY=srH&SXQVcUfX0)cuPPh5Qp`F;@#gKR=J& zbqA|>L&?MB4g4&`D$6?PE+1fj8{n`#0p-;L?f)TEx(DDUVeG&QIki}R_lV%Iu_1Mzn}nxBbUAt_s9+~GKsikX2{w@kA=9DrS8&`SCg~Wx3AEs$$f?lapT32-O$5W6t2^57VjO!c z6w$*$mY$E%`y`%D7efKw3q9_w_ElrBhS(28+o53E9@08s+?|0KST(3$o5Gu{CTh7d zVkWP(+HjaXt7U4ha8|k6RgA=KvEN-pI#CR?%g@9Yq6%3Hy{a_MuMgJF4HOdMgGbm)Z$d+tGt3%IS|`<(DJ$yhf&LKLDt=b+^4pnB{xvZKrd`iDP&+j z1cPWRD9@vn{z?f&Ma(!BoN|xE$y20*k|=$UI?4y+{U8l{;rTaTKLOUfN;ab^(#`1p zU>hF?L7{sW8K!q#EWu#=EYKY`EU1%8$xiFbh#f0jGWeq=;i!X2OzSsiVb)bC=P zc?p&AEIi%+r%q5ysoTLW_Q@v^Qy&JZc3JV1Z;0gp&+lUGullHO)pHH zk=a+zlx>_~e2VyGM@~kR^&a&B5$lp*3QfjcsTA%YmC;8YXa}@e=zV<hE9ArWb_qK zw=s@)(06M*$PoS1Mj$xHBcExVT2ieFKfOxIIJhmW7C-vF2A#QPgw!87X! zw5XxrR>-DKmd2JYRu(hLCVGZQ**T}zFSiv#a@x{H;-BhS8 zE|5NI;W|gYhGN^xnUM3i&6r_$4u82AV`o!uV;$2Z(*UDl8fmgxg1{44<2TR;dK-cd~FJT^XsfKdK1?E`O08_H* zit(oDjiHNSiE+B&vay4aG>t|HY4Df}vOTaLw#C`H*$-GZTgDjM@g{CH6+jGCL**Ib z1m6!KNoXw)LJ%_FFH0@u6LL*uiPA$kBj^6sT=#O1bQUkva@Xdy$sU$vXw8p#ymXW7g!Lj~hJQzyT9mQ~jBmX4+_=6S{i#>&PN&cjdSPZ)yCJ56sa zO|8$NeTcIU^$)R+ur2X>V5Uv4xe9RksHIg^rivSol)-p|kh>f04fSRErb^F12e5NS zt+)K2S*KfDfW)%Md=k3E+WaDB2;Y{!Ww>cBXZmPO@cU`o8ECTi_y1-qu=e&_ZAj&g z(w+4oS}m!rZ-`fM_4fR7PxJKhCVD%IqU5KJ)dpduLPbX3AL?qUuHbSXFLdWt%blKe zG?PppklH=tZbqBDj)j|@ZsCS_lc>m7Fjlk%SktU0{m0nL__ww_Gc`B)_~*#KEL!!2 zvW9J7XlHC=*>4%)H{5@lzZ5v%KgAYni!)~%H?U1fpEg0N;q&t@a7}ephnHTgr@Oa< z?+>{CBqHlH0neI7Jjvz5iMW%mhP#%dZ2tMonwdH2dsFA8wMt1(@1I*gzqMzuuv#iX z-C%xLd~kY7v{$$7^MB&M%#Qn)`hCAptavuB&S{W0}!IqM?VYWX5pBC#G z@YR2h-w|scGaRMp|I|b=*4Nha*j3VTwy=V0m0JQc!6)`p?L>8YHuD8s`H|Fl%t~XG zroMx&K?MhL_hija-IL~*S}#RSKb4Z5v$T+Q4Tn}`9vNm>Zo1(g>fab%S(@Km|95`d z%;6>{XiwYmOn#2pLFQ9ixmkRB%QmRit^T9^zXt9LT;Si-Ho#on6u^xqTWH6?iR|kR z24`YOVY;J-oAC}2FG#~x7SRP08_LEr{>)*NkPPCi$5X=bZ(iw~j5Jru;ItVjvofxy zeN3NKu*+FT{zr?_$8$%G_xxrD9>Y1E_50zs!*CJO3URsQxF4I-mL6$Mr zqk-LukFamB_pqc{_E}egof%K=B2q!XibbC43AzMd)!5%U$ChC8_y+{64cP3@`mMHn zGhAR#Vm)DoD0pJsja|zee>l23?zx5vm83_?c#R~3*?2zBFyC;1tIHM8H7TRsRyyV_ zo#Wn+JHq)F$;!G~@Kxl$b^9`R6)=z=pj-Lo#Gk4%}F zyf68WOgg(>?xws!dE;VILDVcI?b5U?pNc~cG-=xvvN=6 zT+8nyw$N+C#ibr7bRFz{ip2!C3zYpfS@&C4*`lo)Uj{326R9^$1~-8H4%OXvgP)c2 z`)S!=nP=&2>1n=i_{l`Vp}9a(g{SU;&M?;+R|{nMwD!fzz4We(n;UJ|Wn5@}X8mkU zFpo4$VE5Avs6Vjcv>a{F;96DKF~4nYcIKJ%%4t`Vf`5HacV*1Y4#-@e73EncN#sp- zBy-7B+wW;mY{{_V3j-e7_StI$6t`PVb=h3_DvW0LvmtCIl|mbhk49WArXq1NlB zs^)=wA9@OiW`D}7yg9C^xTAN&xYSNqCGv;~hw{3CF_kraHs^z0I?&S8@Sdr|JfjAY zJv2`4?8`&Ge>r!Wqi_D8oO9Xl)7X@$Kb!w(knTyTmsXHACN0)ePhm)tNn#&bTLcO3C9Z8Cr^JdN}Qrrq&FKUS$|nL+@<4earPyC6-}MFP00U-x2|qb+KR1x zQ+%Q@*z?UbsqlSX+uTMOUsF6kr+hUh{YoB?R4q0CN9o)@r48ycgTG;mrDn*(&<~OA zqbih)4$d#urkF4AKT8#66C!ry$Ysz1@VxvZxujNv87FueN!9U2OlepSC}-)xyKm?b08rJ(R!Tq4`OQ zluC-Ukl^$y{4YB^^ImG7#3x^NyiNU7`}?Kui<145qjJ5H&YtkA8qlx!ozh!l9#x!D z{z=5f;0ML`1oiVjYgV9(=z^8unqbp&dSfDjS<1gO1sRVR#`AZW+f-ex2WqLV=cY5f z@L~Q|hrzSZw_K^KeWRBeqy2gY#=+h4yZxv=&EM!Z+Bk)ofYqTq%zf&sKDdilRF7#_ z)Q9lr)r1^ZyMk3&eA<*>4Ze2&aQ*e;XD2^#iQj(2q?Jr7BkV9dwvQ@Bl@`matni^q zsT#$q434-Rd?e_l|0h45Pf!-YZEU_&3XIed;C$XE+i=&o|M4SP33t6rr7>J%5&3ZU zbGY)mIg(uW#l6~e;wT$ps$v@!^s>aK5+TI||Lgvv>_05WxLZUU^|d?;tJ|$*pO`JN z@c2p5`fKgfW#UfH|D2AzjEqUi<-c$KeDdv|ue!fj{x0$R{*+JI>+`SsCR(P4WJXnq zSsg=E9$T$Z&6KLf=#n81?UB|-rU3f86yv_-%yG5!{VgYh=3GmgOG#je^dQ5@dRm+~ zQux<%(M`EpINmu1I0@l}I*iHS9~!&ZDwil#Dxfq~DkC^E=&Ao(+hR*+cA1tSHum-R zIfZ?`1-@fqw0s`x%66iho~|VMyzc3Rr}9Q;y!kopYqO6tKWuqB^!4GlUlWgHY;*A5 zICX`I4aq1MT8XJ7SA0_OpBkpB6{8=7RNM>M z*@K=$Y}5>LXb5?D{)Hj{Cw+YM-epc#3M91j#(xD;2#Xi}` z`+eg5$W7vB_Ym+x?ts*D*}GP>D6{ni$aIuICJt1#iNT&>g*S4(ryofgllcAXiO-$B zzWH?eGxzQOuT)oKIfht99yCUjx=}W%;)n8YqMF29s&Ki&h3HWwewnNA@9Ae`ZKa)i zOhHP1q_do_o!Ct1EDV#zVs&@5vRdj6X7z4&bJugnYR4+)Lbv99C$85rx#PCNVjD|* z3TaxVAgWIEuJFLn@I-z;yna>@ z#Uu~wIFef;b8+g^q&`3A{HXr@$#?C0a?;3O{yE1aPMbo1WR6-IgpY`>R{nYU{*jBz zu8yI~wvT)q@PThZSJhjn{e`0i4{{T88W$Y)tnyA2>Ik)@3s@l)pl7Qt1`1={4P8&2 z4V;X7r)RO?r=6yInUe#w65UIm4vUPKR`yqPMEL38JpbF)w!BI*$|BGC!kziyjtp0h zyR`SNuQ?Q3$LV2g2euL->Sgpx@&?~6=i-9h*#|Pdr!`28NI8?#CAq^dQ|hVoPpOad zYsqos9DWV;z*4dFuPCBoX!MqdxzV4)r$@FdU0`d^j+73-;W*J@%3YN{D)UGF(!#}p z?Az?yp&VB?!9`q=jq*1k-gU+Gmovsq2~E5uC7%4ruk~+S;!5er$k_0tsBdMTN4+oI zHE@u%12+qiShM$>V@+;OPP4)kHz~e@=6yZ6ilt3n^EJ~o?gU+rIID(A13UvAUyzse z5&3#wvvy{b$()?FDq~Sr$IRWC^-`XtmUV|p$yl|WptlR$R(gL-y^4pT!ehRaZ5gvU zCM=|fbuiIfJneg5crm9;YLj2rGG=8>goHaZ3g+ZB%d zA5)9>SVX^3*01JW{0%0XNR~HP*phPsqby!t?6r}9Hm?OWfyxHGPaZ&E=8@0 zniEmBRMX-Otg-BVEmkwvFXwcu#J^B9Twl>;-t$mjzoZpCuZV$yL zoOU%Tn3P|y;ABB%$5OEVdOK^oF1r467Uuqyu_+}zY0S@2U;UGU3pTpfD>t<=rXykd zV#?K+QfqCsB~`apSzX0kHX*PT{aR?_n3#P&<;0JZU!HucpZqH8o@0tfkl?h#wlv)2 z?;tmEuKq}BDID%ZQ-bp`AjHl{^=!hn5MgQ+$p8DI-U$SDK0j zaj~mOK~+ad?kAePG1E$ zqV1G=h!w@7LW9Eh*~YZ^)Y+-We?Cs?^J{P3a`CbrX-c!$iyw(vSl++NwsNhaYL|@+ zZyx%{UYD(=RrRiRh7=6UOh`9pe$1KYJmzjCE>-I4Etr$Y!pDz;5zRO00-`(vq!>?Pt*8a!6_ILR zxQa~^ox)q`2vqLz@&$1R)<0g#N5yp@t9A9%^Q5>Rz(F|EGtpaG3>Mpojl|i$%a}`9 zJ%c>ET?fS!#>Vx7zd(K4snYes|BE;kb~2g&l91%z(~_1gHl(1<0>ZOVuqT(ZyOZ6=|Euc4h(3hS&D(6(j?UYT)jgxn$#%Gr) zyd+dohmkgJfuWcw+EmlzG_+;jXb0h3R!rRs?$bfIGCVW1_gfrLuh>EVPW}!2K9~#n z|B+Lb2=7Zr^MWh6HSdfiXMnjl2J>YXOqyIwNMRRN8 zFnHmZ#Kq3H`8V_D=AFquR`|w!Px_#hWGkBHS%=#<`ses>vboKqDTV()x6vD`hm;G- z7-X5vBYRR!nESj38QdhBh`8!AEkR!CZRuL?Xylmh^mA@?K6P#MycACPhKav?3&D@v z;2P<$^@+j_B*87vd$cBoq>}$(r-+Ki@#LAr;S4CcCYqf4cpst%K#ADUpA` z3?#2ANzzfNgi=R!DYxO@T9ci|=NKLtDscOmD$H8M_iu_9(MG2Vt>L|F6u%%t;JG?m zd!WrygOsIG3ttQ2hwG2R?RmGe|IGSd=K1uvOg`@)nTGD3H)L6xm6=@ba8y=gc$xO4 z8O|$e!h>OT zMy&zIt^QD?Zh#kIC1M3M@M&TrUo*Ids*Y6-gCnspt01%BRY5{QwSu;A33%>4Lcib| zvAa!w``rpt!@02j5r>1W7ke1E)7sLwO`PGJ<;c$&og18O%o&q^CpXZ&Lpr9_r$!Om zm>_N#-JCg2j-|5oqjEXKF1C^$Y7_l{FKrlaVy*F()n<|N&{L>U+F6ce`tJH~k9Qe!VeEE~W!p;;;&elCxYw=@b$-b%z|vON_7Qu{bMiK+?x zc}Hj~m*`cXdS9X6Blpn7*nimmY-hOno@c8wgQ#-E4)CqJfU7yiH%w?KqzDg$>At_j zgTDQ~1fNeBE_`(NbjP?79DVSlt+?*0jrnE#3e3M66`vS3F4R_%FP?3GW4U4of=YD0 zt6l#2{IZ2Yo+p1(;WXE6;fXBhRVXXH3m&OW=;nwejv;7dS4@C5AHHo=S#BoxoLj?h zFy@+z83pDR8KryF)AIksX@c7mB7E{4mKG}awG22gKWARDL%4SQIxd+>qV~d@t3Xdc zuG>&(LY;_O^n!n6DpiVpM?Iv_;`Q3vKgvyc8=RmO+_g$+|B@X+YjwkkcB)~FVJ%;i zyFt_BUfrT4DtqNKQVZnFeNcMC0gQ!iCW06U2eH1|TV<=fTq-Bl5PSQ!3U=Q)5CFG$ zFM68`9ffe;dbK#0$}MKj^3Sb~V$*~6+I!nvrf6di<9xb?W)Zfy9)P|X?GAU9aiTuEQNdLh7{0;d6F}!{H6YXEEjP_C61ZBfE zc)OKCl(Gg%y#~h z@hUi&;oLmBHx)??L~hX+-)9iF(}dCR$GsvqRTpXfz@_U*4WxI`L#eCK)a-#fW+B`> zr$RqG1iqB7k(qFpbdfKhtGETq`%C?WRtuR*{ne{#9r%+DKz0@nN#1@CRew=CsE(LP zRwwTv_TL*^$*tOcu!%UhA9mLhpr&XB0$nR;SDu0Owh2y~gAlcy3G&(&M2bfvmYWA< z+&AJfT2?M`iC6)ql|Nh`Cm|EL6JppDT8KgSgP!0km`2Ia%nb$QZwKPoJrK$6h&J>b zdu^kShDL$bheK`f5dTjE>7)rX22uFw09{4_R3h7<4>O=u#v{hN3HpsYzZqMX&<-~u zKHZtPf<2{Uo85?ZA4Fc{2WTnYAR4+&d!prP&yj&6!B4R{w(g2r;h?H`itC6)D_;Ut zfQAV9zlf$6MTg@MD^J7z&%!HZKVtPeK)T|fBU_Qg9Lhzd~BeF3-WHcDeKe(oWP&B4E;KUjYAP>ch3M{FJ7;r ze+7F{g;F>L#KNJNJuU*T>lc(dM{yq6i0z+38=8h1$$`eL3HH$stJYT1Nz{cRgu7ZM4l4 zC|b%Q`=BMxzDS3!;BONcbEznaFHltE!8fKhMi0YpDjbFLeu*5!El>_DM!Q}K?ZHay zX#@843)x^rd@>6NP&!K8176i@kl)toyTA{;jCODmuYJg~IggT0*G04qhaQ4s+J4i> zN+G|dhlbu4w|6+J0a@+2h_^TS!2rBrHrP1C>qIOC{2a*TvhYwsh zH`)Odj38eoAR6+N&muDlgu)8pYFI|6lc5N@ASQ97}>iV-NI z<@hV%cRNqOxn2QrFabSaGgKI7uwQt1;s~EXK+A$gWd*+9U!Q?e+=@>xBSKvhHGBYV z?I84N@MJ-4FFIR^)GXW| zN1+8Y#VZ0T%U4ixErfO`5^H!V(3T|s zX7ioHe6Ofq-1tot%!O~Nfp~>|q@u^Z!C7VE+>T;PC+aj8^&fyb5^+s5^ei7y7k5$P zAFy>c$}AZkw?%YAKaf71ICcTv|BCZ+qlXyKbJ`J;!G>Inl3M{K+HCyW4S8^7P+B!X z(GzgiMbx??u3nJ}^gXWYKI-~9+TTyq%X_HqEGV}k)k{}=vmbspL)#9=9!i0o==b}~ z2<#^VEwG5<=f+um!8sK1(P(J9iujovwv_Nr0ge@bcZ;ZoEHq~-w$U-t@=!N;a3%RD zKN8pBL5T$87~#k+u8KOe Date: Tue, 8 Jul 2025 08:13:05 +0000 Subject: [PATCH 02/21] add model config --- .../modules/text_to_pinyin/G2PWModel.yml | 11 +++++++++ .../fastspeech2_csmsc.yaml | 22 ++++++++++++++++++ .../text_to_speech_vocoder/pwgan_csmsc.yaml | 22 ++++++++++++++++++ test.wav | Bin 73844 -> 0 bytes 4 files changed, 55 insertions(+) create mode 100644 paddlex/configs/modules/text_to_pinyin/G2PWModel.yml create mode 100644 paddlex/configs/modules/text_to_speech_acoustic/fastspeech2_csmsc.yaml create mode 100644 paddlex/configs/modules/text_to_speech_vocoder/pwgan_csmsc.yaml delete mode 100644 test.wav diff --git a/paddlex/configs/modules/text_to_pinyin/G2PWModel.yml b/paddlex/configs/modules/text_to_pinyin/G2PWModel.yml new file mode 100644 index 0000000000..070341f731 --- /dev/null +++ b/paddlex/configs/modules/text_to_pinyin/G2PWModel.yml @@ -0,0 +1,11 @@ +Global: + model: G2PWModel + mode: predict # only support predict + device: gpu:0 + output: "output" + +Predict: + batch_size: 1 + input: "欢迎使用飞桨" + kernel_option: + run_mode: paddle \ No newline at end of file diff --git a/paddlex/configs/modules/text_to_speech_acoustic/fastspeech2_csmsc.yaml b/paddlex/configs/modules/text_to_speech_acoustic/fastspeech2_csmsc.yaml new file mode 100644 index 0000000000..65e5067892 --- /dev/null +++ b/paddlex/configs/modules/text_to_speech_acoustic/fastspeech2_csmsc.yaml @@ -0,0 +1,22 @@ +Global: + model: fastspeech2_csmsc + mode: predict # only support predict + device: gpu:0 + use_trt: False + use_mkldnn: False + cpu_threads: 1 + precision: "fp32" + output: "output" + model_name: "fastspeech2_csmsc" + speaker_dict: None + lang: zh + speaker_id: 0 + +Predict: + batch_size: 1 + model_dir: "fastspeech2csmsc" + input: "今天天气真不错" + lang: zh + speaker_id: 0 + kernel_option: + run_mode: paddle \ No newline at end of file diff --git a/paddlex/configs/modules/text_to_speech_vocoder/pwgan_csmsc.yaml b/paddlex/configs/modules/text_to_speech_vocoder/pwgan_csmsc.yaml new file mode 100644 index 0000000000..16debe45f0 --- /dev/null +++ b/paddlex/configs/modules/text_to_speech_vocoder/pwgan_csmsc.yaml @@ -0,0 +1,22 @@ +Global: + model: pwgan_csmsc + mode: predict # only support predict + device: gpu:0 + use_trt: False + use_mkldnn: False + cpu_threads: 1 + precision: "fp32" + output: "output" + model_name: "pwgan_csmsc" + speaker_dict: None + lang: zh + speaker_id: 0 + +Predict: + batch_size: 1 + model_dir: "pwgan_csmsc" + input: "今天天气真不错" + lang: zh + speaker_id: 0 + kernel_option: + run_mode: paddle \ No newline at end of file diff --git a/test.wav b/test.wav deleted file mode 100644 index 207e5e9729db4b19f62b5d4d97fc1ba08589a846..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73844 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 Date: Wed, 16 Jul 2025 02:32:22 +0000 Subject: [PATCH 03/21] config and readme --- .../speech_modules/text_to_pinyin.en.md.md | 182 +++++++++++++++ .../speech_modules/text_to_pinyin.md | 179 +++++++++++++++ .../speech_modules/text_to_speech_acoustic.md | 206 +++++++++++++++++ .../text_to_speech_acoustic_en.md | 208 +++++++++++++++++ .../speech_modules/text_to_speech_vocoder.md | 191 ++++++++++++++++ .../text_to_speech_vocoder_en.md | 209 ++++++++++++++++++ .../fastspeech2_csmsc.yaml | 2 +- .../text_to_speech_vocoder/pwgan_csmsc.yaml | 2 +- .../text_to_speech_vocoder/predictor.py | 6 +- 9 files changed, 1182 insertions(+), 3 deletions(-) create mode 100644 docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md.md create mode 100644 docs/module_usage/tutorials/speech_modules/text_to_pinyin.md create mode 100644 docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.md create mode 100644 docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic_en.md create mode 100644 docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.md create mode 100644 docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder_en.md diff --git a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md.md b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md.md new file mode 100644 index 0000000000..2edde231b8 --- /dev/null +++ b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md.md @@ -0,0 +1,182 @@ +--- +comments: true +--- + +# Tutorial for Text To Pinyin Module + +## I. Overview +Text to Pinyin is commonly used in the frontend of TTS to convert input Chinese text into a phonetic sequence with tones, providing pronunciation basis for subsequent acoustic models and audio generation. + +## II. Supported Model List + + + + + + + + + + + + + + +
ModelDownload linkModel sizeIntroduction
G2PWModelG2PWModel606M g2pW is an open-source text to pinyin model, commonly used in the frontend of TTS. It converts input Chinese text into a tonal Pinyin sequence, providing pronunciation basis for subsequent acoustic models and audio generation
+ +## III. Quick Integration +Before quick integration, you need to install the PaddleX wheel package. For the installation method, please refer to the [PaddleX Local Installation Tutorial](../../../installation/installation.en.md). After installing the wheel package, a few lines of code can complete the inference of the text to pinyin module. You can switch models under this module freely, and you can also integrate the model inference of the text to pinyin module into your project. + + +```python +from paddlex import create_model +model = create_model(model_name="G2PWModel") +output = model.predict(input="欢迎使用飞桨", batch_size=1) +for res in output: + res.print() + res.save_to_json(save_path="./output/res.json") +``` + +After running, the result obtained is: + +```bash +{'res': {'input_path': '欢迎使用飞桨', 'result': ['huan1', 'ying2', 'shi3', 'yong4', 'fei1', 'jiang3']}} +``` + +The meanings of the runtime parameters are as follows: +- `input_path`: The storage path of the input text. +- `result`: Pinyin converted from the input text. + +Related methods, parameters, and explanations are as follows: +* `create_model` for text to pinyin model, with specific explanations as follows: + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterDescriptionTypeOptionsDefault Value
model_nameThe name of the modelstrG2PWModelG2PWModel
model_dirThe storage path of the modelstrNoneNone
+ +* The `model_name` must be specified. After specifying `model_name`, the built-in model parameters of PaddleX are used by default. If `model_dir` is specified, the user-defined model is used. + +* The `predict()` method of the text to pinyin model is called for inference and prediction. The parameters of the `predict()` method are `input` and `batch_size`, with specific explanations as follows: + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterDescriptionTypeOptionsDefault Value
inputData to be predictedstr +
    +
  • Input text, such as: 欢迎使用飞桨
  • +
+
None
batch_sizeBatch sizeintCurrently only supports 11
+ +* The prediction results are processed as `dict` type for each sample and support the operation of saving as a `json` file: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodDescriptionParameterParameter TypeParameter DescriptionDefault Value
print()Print the result to the terminalformat_jsonboolWhether to format the output content with JSON indentationTrue
indentintSpecify the indentation level to beautify the output JSON data, making it more readable. This is only effective when format_json is True4
ensure_asciiboolControl whether to escape non-ASCII characters to Unicode. When set to True, all non-ASCII characters will be escaped; False retains the original characters. This is only effective when format_json is TrueFalse
save_to_json()Save the result as a file in json formatsave_pathstrThe file path for saving. When it is a directory, the saved file name will match the input file nameNone
indentintSpecify the indentation level to beautify the output JSON data, making it more readable. This is only effective when format_json is True4
ensure_asciiboolControl whether to escape non-ASCII characters to Unicode. When set to True, all non-ASCII characters will be escaped; False retains the original characters. This is only effective when format_json is TrueFalse
+ +* Additionally, the prediction results can also be obtained through attributes, as follows: + + + + + + + + + + + + +
AttributeDescription
jsonGet the prediction result in json format
+ +For more information on using PaddleX's single-model inference APIs, please refer to the [PaddleX Single-Model Python Script Usage Instructions](../../instructions/model_python_API.en.md). \ No newline at end of file diff --git a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md new file mode 100644 index 0000000000..1141c45c30 --- /dev/null +++ b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md @@ -0,0 +1,179 @@ +--- +comments: true +--- + +# 文本转拼音模块使用教程 + +## 一、概述 +文本到拼音常用于语音合成的前端,将输入的中文文本转换为带声调的拼音序列,为后续的声学模型和模型生成提供发音依据。 + +## 二、支持模型列表 + + + + + + + + + + + + + + +
模型模型下载链接模型大小介绍
G2PWModelG2PWModel606M g2pW 开源的文本到拼音模型,常用于语音合成的前端,将输入的中文文本转换为带声调的拼音序列,为后续的声学模型和模型生成提供发音依据
+ +## 三、快速集成 +在快速集成前,首先需要安装 PaddleX 的 wheel 包,wheel的安装方式请参考[PaddleX本地安装教程](../../../installation/installation.md)。完成 wheel 包的安装后,几行代码即可完成文本转拼音模块的推理,可以任意切换该模块下的模型,您也可以将文本转拼音模块中的模型推理集成到您的项目中。 + +```python +from paddlex import create_model +model = create_model(model_name="G2PWModel") +output = model.predict(input="欢迎使用飞桨", batch_size=1) +for res in output: + res.print() + res.save_to_json(save_path="./output/res.json") +``` +运行后,得到的结果为: +```bash +{'res': {'input_path': '欢迎使用飞桨', 'result': ['huan1', 'ying2', 'shi3', 'yong4', 'fei1', 'jiang3']}} +``` +运行结果参数含义如下: +- `input_path`: 输入文本 +- `result`: 输入文本转换后的拼音 + +相关方法、参数等说明如下: +* `create_model`文本转拼音模型,具体说明如下: + + + + + + + + + + + + + + + + + + + + + + + + +
参数参数说明参数类型可选项默认值
model_name模型名称strG2PWModelG2PWModel
model_dir模型存储路径str
+ +* 其中,`model_name` 必须指定,指定 `model_name` 后,默认使用 PaddleX 内置的模型参数,在此基础上,指定 `model_dir` 时,使用用户自定义的模型。 + +* 调用文本转拼音模型的 `predict()` 方法进行推理预测,`predict()` 方法参数有 `input` 和 `batch_size`,具体说明如下: + + + + + + + + + + + + + + + + + + + + + + + + + +
参数参数说明参数类型可选项默认值
input待预测数据str +
    +
  • 对应文本,如:欢迎使用飞桨
  • +
+
batch_size批大小int目前仅支持11
+ +* 对预测结果进行处理,每个样本的预测结果均为对应的Result对象,支持保存为`json`文件的操作: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
方法方法说明参数参数类型参数说明默认值
print()打印结果到终端format_jsonbool是否对输出内容进行使用 JSON 缩进格式化True
indentint指定缩进级别,以美化输出的 JSON 数据,使其更具可读性,仅当 format_jsonTrue 时有效4
ensure_asciibool控制是否将非 ASCII 字符转义为 Unicode。设置为 True 时,所有非 ASCII 字符将被转义;False 则保留原始字符,仅当format_jsonTrue时有效False
save_to_json()将结果保存为json格式的文件save_pathstr保存的文件路径,当为目录时,保存文件命名与输入文件类型命名一致
indentint指定缩进级别,以美化输出的 JSON 数据,使其更具可读性,仅当 format_jsonTrue 时有效4
ensure_asciibool控制是否将非 ASCII 字符转义为 Unicode。设置为 True 时,所有非 ASCII 字符将被转义;False 则保留原始字符,仅当format_jsonTrue时有效False
+ +* 此外,也支持通过属性获取预测结果,具体如下: + + + + + + + + + + + + + +
属性属性说明
json获取预测的json格式的结果
+ +关于更多 PaddleX 的单模型推理的 API 的使用方法,可以参考[PaddleX单模型Python脚本使用说明](../../instructions/model_python_API.md)。 \ No newline at end of file diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.md new file mode 100644 index 0000000000..a682ad5425 --- /dev/null +++ b/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.md @@ -0,0 +1,206 @@ +--- +comments: true +--- + +# 语音合成声学模型使用教程 + +## 一、概述 +语音合成声学模型是语音合成技术的核心组件,其特点在于通过深度学习等技术将文本转化为逼真的语音输出,并支持细粒度控制语速、韵律等特征。主要应用于智能语音助手、导航播报、影视配音等领域。 + +## 二、支持模型列表 + +### Fastspeech Model + + + + + + + + + + + + + + + +
模型模型下载链接训练数据模型存储大小(MB)介绍
fastspeech2_csmscfastspeech2_csmsc\\157MFastSpeech2 是微软开发的端到端文本转语音(TTS)模型,具备高效稳定的韵律控制能力。它采用非自回归架构,能实现快速高质量的语音合成,适用于虚拟助手、有声读物等多种场景。
+ +## 三、快速集成 +在快速集成前,首先需要安装 PaddleX 的 wheel 包,wheel的安装方式请参考[PaddleX本地安装教程](../../../installation/installation.md)。完成 wheel 包的安装后,几行代码即可完成多语种语音合成声学模块的推理,可以任意切换该模块下的模型,您也可以将多语种语音合成模块中的模型推理集成到您的项目中。 + + +```python +from paddlex import create_model +model = create_model(model_name="fastspeech2_csmsc") +output = model.predict(input=[151, 120, 182, 82, 182, 82, 174, 75, 262, 51, 37, 186, 38, 233]. , batch_size=1) +for res in output: + res.print() + res.save_to_json(save_path="./output/res.json") +``` +运行后,得到的结果为: +```bash +{'result': array([[-2.96321 , ..., -4.552117 ], + ..., + [-2.0465052, ..., -3.695221 ]], dtype=float32)} +``` +运行结果参数含义如下: +- `input_path`: 输入音频存放路径 +- `result`: 输出mel谱结果 + +相关方法、参数等说明如下: +* `create_model`多语种识别模型(此处以`fastspeech2_csmsc`为例),具体说明如下: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数参数说明参数类型可选项默认值
model_name模型名称strfastspeech2_csmsc
model_dir模型存储路径str
device模型推理设备str支持指定GPU具体卡号,如“gpu:0”,其他硬件具体卡号,如“npu:0”,CPU如“cpu”。gpu:0
use_hpip是否启用高性能推理插件。目前暂不支持。boolFalse
hpi_config高性能推理配置。目前暂不支持。dict | NoneNone
+ +* 其中,`model_name` 必须指定,指定 `model_name` 后,默认使用 PaddleX 内置的模型参数,在此基础上,指定 `model_dir` 时,使用用户自定义的模型。 + +* 调用多语种语音识别模型的 `predict()` 方法进行推理预测,`predict()` 方法参数有 `input` 和 `batch_size`,具体说明如下: + + + + + + + + + + + + + + + + + + + + + + + + + +
参数参数说明参数类型可选项默认值
input待预测数据str +
    + 输入的input_phone_ids, 目前只支持tensor类型,如[151, 120, 182, 82, 182, 82, 174, 75, 262, 51, 37, 186]等 +
+
batch_size批大小int目前仅支持11
+ +* 对预测结果进行处理,每个样本的预测结果均为对应的Result对象,支持保存为`json`文件的操作: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
方法方法说明参数参数类型参数说明默认值
print()打印结果到终端format_jsonbool是否对输出内容进行使用 JSON 缩进格式化True
indentint指定缩进级别,以美化输出的 JSON 数据,使其更具可读性,仅当 format_jsonTrue 时有效4
ensure_asciibool控制是否将非 ASCII 字符转义为 Unicode。设置为 True 时,所有非 ASCII 字符将被转义;False 则保留原始字符,仅当format_jsonTrue时有效False
save_to_json()将结果保存为json格式的文件save_pathstr保存的文件路径,当为目录时,保存文件命名与输入文件类型命名一致
indentint指定缩进级别,以美化输出的 JSON 数据,使其更具可读性,仅当 format_jsonTrue 时有效4
ensure_asciibool控制是否将非 ASCII 字符转义为 Unicode。设置为 True 时,所有非 ASCII 字符将被转义;False 则保留原始字符,仅当format_jsonTrue时有效False
+ +* 此外,也支持通过属性获取预测结果,具体如下: + + + + + + + + + + + + + +
属性属性说明
json获取预测的json格式的结果
+ +关于更多 PaddleX 的单模型推理的 API 的使用方法,可以参考[PaddleX单模型Python脚本使用说明](../../instructions/model_python_API.md)。 diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic_en.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic_en.md new file mode 100644 index 0000000000..8ab7678cc7 --- /dev/null +++ b/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic_en.md @@ -0,0 +1,208 @@ +--- +comments: true +--- + +# 语音合成声学模型使用教程 + +## I. Overview +The acoustic model for speech synthesis is the core component of speech synthesis technology. Its key characteristic lies in utilizing deep learning and other techniques to transform text into lifelike voice output while enabling fine-grained control over features such as speech rate and prosody. It is primarily applied in fields such as intelligent voice assistants, navigation announcements, and film and television dubbing. + +## Supported Model List + +### Fastspeech Model + + + + + + + + + + + + + + + +
ModelDownload linktraining dataIntroductionModel Storage Size (MB)
fastspeech2_csmscfastspeech2_csmsc\\157MFastSpeech2 is an end-to-end text-to-speech (TTS) model developed by Microsoft, featuring efficient and stable prosody control. It adopts a non-autoregressive architecture that enables fast and high-quality speech synthesis, suitable for various scenarios such as virtual assistants and audiobooks.
+ +## 3. Quick Integration +Before quick integration, first install the PaddleX wheel package. For wheel installation methods, please refer to [PaddleX Local Installation Tutorial](../../../installation/installation.md). After installing the wheel package, inference for the multilingual speech synthesis acoustic module can be completed with just a few lines of code. You can freely switch models within this module, or integrate model inference from the multilingual speech synthesis module into your project. + + +```python +from paddlex import create_model +model = create_model(model_name="fastspeech2_csmsc") +output = model.predict(input=[151, 120, 182, 82, 182, 82, 174, 75, 262, 51, 37, 186, 38, 233]. , batch_size=1) +for res in output: + res.print() + res.save_to_json(save_path="./output/res.json") +``` +After running, the results are: +```bash +{'result': array([[-2.96321 , ..., -4.552117 ], + ..., + [-2.0465052, ..., -3.695221 ]], dtype=float32)} +``` + +The meanings of the running result parameters are as follows: + +- `input_path`: Input audio storage path +- `result`: Output mel spectrogram result + + +Explanations of related methods and parameters are as follows: + +* `create_model`multilingual recognition model (using `fastspeech2_csmsc` as an example), details are as follows: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterParameter DescriptionTypeOptionsDefault Value
model_nameThe name of the modelstrfastspeech2_csmscNone
model_dirThe storage path of the modelstrNoneNone
deviceModel inference devicestrSupports specifying specific GPU card numbers (e.g. "gpu:0"), other hardware card numbers (e.g. "npu:0"), or CPU (e.g. "cpu")gpu:0
use_hpipWhether to enable high-performance inference plugin. Currently not supported.boolNoneFalse
hpi_configHigh-performance inference configuration. Currently not supported.dict | NoneNoneNone
+ +* Among these, `model_name` must be specified. After specifying `model_name`, PaddleX's built-in model parameters are used by default. When `model_dir` is specified, the user-defined model is used. +* Call the multilingual speech synthesis model's `predict()` method for inference prediction. The `predict()` method parameters include `input` and `batch_size`, with details as follows: + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterParameter DescriptionTypeOptionsDefault
inputData to be predictedstr +
    + Currently only supports tensor-type input_phone_ids, such as [151, 120, 182, 82, 182, 82, 174, 75, 262, 51, 37, 186], etc. +
+
None
batch_sizebatch sizeintCurrently only 1 is supported1
+ +* Process the prediction results. The prediction result for each sample is a corresponding Result object, which supports saving as `json` files: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodMethod DescriptionParameterTypeParameter DescriptionDefault
print()Print results to terminalformat_jsonboolWhether to format the output content with JSON indentationTrue
indentintSpecifies the indentation level to beautify output JSON data for better readability. Only effective when format_json is True4
ensure_asciiboolControls whether to escape non-ASCII characters to Unicode. When set to True, all non-ASCII characters will be escaped; False preserves original characters. Only effective when format_json is TrueFalse
save_to_json()Save results as json filesave_pathstrFile save path. When it is a directory, the file name follows the input file type naming conventionNone
indentintSpecifies the indentation level to beautify output JSON data for better readability. Only effective when format_json is True/td> +4
ensure_asciiboolControls whether to escape non-ASCII characters to Unicode. When set to True, all non-ASCII characters will be escaped; False preserves original characters. Only effective when format_json is TrueFalse
+ +* Additionally, the prediction results can also be obtained through attributes: + + + + + + + + + + + + + +
AttributeDescription
jsonGet prediction results in json format
+ +For more information on using PaddleX's single-model inference APIs, please refer to the [PaddleX Single-Model Python Script Usage Instructions](../../instructions/model_python_API.en.md). diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.md new file mode 100644 index 0000000000..9ed5b534f2 --- /dev/null +++ b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.md @@ -0,0 +1,191 @@ +--- +comments: true +--- + +# 语音合成声学模型使用教程 + +## 一、概述 +语音合成声码器是语音合成技术的关键组件,其特点在于利用信号处理算法将声学模型生成的频谱参数转化为可播放的波形音频,并支持调整音色、音高等音质特征。主要应用于语音合成系统的后端处理、音效优化及个性化语音生成等领域。 + +## 二、支持模型列表 + +### Fastspeech Model + + + + + + + + + + + + + + + + +
模型模型下载链接训练数据模型存储大小(MB)介绍
pwgan_csmscpwgan_csmsc\\5.1MParallel WaveGAN(PWGAN)是日本名古屋大学开发的端到端语音合成声码器,具备高效稳定的波形生成能力。它采用生成对抗网络架构,能实现快速高保真的语音重建,适用于TTS系统后端处理、语音增强等多种场景。
+ +## 三、快速集成 +在快速集成前,首先需要安装 PaddleX 的 wheel 包,wheel的安装方式请参考[PaddleX本地安装教程](../../../installation/installation.md)。完成 wheel 包的安装后,几行代码即可完成多语种语音合成声学模块的推理,可以任意切换该模块下的模型,您也可以将多语种语音合成模块中的模型推理集成到您的项目中。 + + +```python +from paddlex import create_model +model = create_model(model_name="pwgan_csmsc") +output = model.predict(input='./mel.npy'. , batch_size=1) +for res in output: + res.print() + res.save_to_json(save_path="./output/res.json") +``` +运行后,得到的结果为: +```bash +{'result': array([-9.085755e-04, ..., 8.858787e-05], dtype=float32)} +``` +运行结果参数含义如下: +- `result`: 输出wav音频数据,类型为`numpy.ndarray`。 + +相关方法、参数等说明如下: +* `create_model`语音合成声码器(此处以`pwgan_csmsc`为例),具体说明如下: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
参数参数说明参数类型可选项默认值
model_name模型名称strpwgan_csmsc
model_dir模型存储路径str
device模型推理设备str支持指定GPU具体卡号,如“gpu:0”,其他硬件具体卡号,如“npu:0”,CPU如“cpu”。gpu:0
use_hpip是否启用高性能推理插件。目前暂不支持。boolFalse
hpi_config高性能推理配置。目前暂不支持。dict | NoneNone
+ +* 其中,`model_name` 必须指定,指定 `model_name` 后,默认使用 PaddleX 内置的模型参数,在此基础上,指定 `model_dir` 时,使用用户自定义的模型。 + +* 调用多语种语音识别模型的 `predict()` 方法进行推理预测,`predict()` 方法参数有 `input` 和 `batch_size`,具体说明如下: + + + + + + + + + + + + + + + + + + + + + + + + + +
参数参数说明参数类型可选项默认值
input待预测数据str +
    + 输入的mel谱, 可以是npy文件路径或者tensor类型。 +
+
batch_size批大小int目前仅支持11
+ +* 对预测结果进行处理,每个样本的预测结果均为对应的Result对象,支持保存为`json`文件的操作以及保存为`wav`文件的操作: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
方法方法说明参数参数类型参数说明默认值
print()打印结果到终端format_jsonbool是否对输出内容进行使用 JSON 缩进格式化True
indentint指定缩进级别,以美化输出的 JSON 数据,使其更具可读性,仅当 format_jsonTrue 时有效4
ensure_asciibool控制是否将非 ASCII 字符转义为 Unicode。设置为 True 时,所有非 ASCII 字符将被转义;False 则保留原始字符,仅当format_jsonTrue时有效False
save_to_audio()将结果保存为wav格式的文件save_pathstr保存的文件路径,当为目录时,保存文件命名与输入文件类型命名一致
+ +* 此外,也支持通过属性获取预测结果,具体如下: + + + + + + + + + + + + + +
属性属性说明
json获取预测的json格式的结果
+ +关于更多 PaddleX 的单模型推理的 API 的使用方法,可以参考[PaddleX单模型Python脚本使用说明](../../instructions/model_python_API.md)。 diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder_en.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder_en.md new file mode 100644 index 0000000000..14c3041577 --- /dev/null +++ b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder_en.md @@ -0,0 +1,209 @@ +--- +comments: true +--- + +# Tutorial for Text to Speech Vocoder + +## I. Overview +The speech synthesis vocoder is a key component of speech synthesis technology. It utilizes signal processing algorithms to convert spectral parameters generated by acoustic models into playable waveform audio, while supporting adjustments of audio characteristics such as timbre and pitch. It is primarily applied in backend processing of speech synthesis systems, audio effect optimization, and personalized voice generation. + +## II. Supported Model List + +### Fastspeech Model + + + + + + + + + + + + + + + +
ModelDownload linkTraining DataModel Storage Size (MB)Introduction
pwgan_csmscpwgan_csmsc\\5.1MParallel WaveGAN (PWGAN) is an end-to-end speech synthesis vocoder developed by Nagoya University, Japan, featuring efficient and stable waveform generation capabilities. It adopts a generative adversarial network architecture that enables fast and high-fidelity speech reconstruction, suitable for various scenarios such as TTS system backend processing and speech enhancement.
+ +## III. Quick Integration +Before quick integration, first install the PaddleX wheel package. For wheel installation methods, please refer to [PaddleX Local Installation Tutorial](../../../installation/installation.md). After installing the wheel package, inference for the multilingual speech synthesis acoustic module can be completed with just a few lines of code. You can freely switch models within this module, or integrate model inference from the multilingual speech synthesis module into your project. + + +```python +from paddlex import create_model +model = create_model(model_name="pwgan_csmsc") +output = model.predict(input='./mel.npy'. , batch_size=1) +for res in output: + res.print() + res.save_to_json(save_path="./output/res.json") +``` +After running, the results are +```bash +{'result': array([-9.085755e-04, ..., 8.858787e-05], dtype=float32)} +``` +The meanings of the running result parameters are as follows: +- `result`: Output wav audio data, of type `numpy.ndarray`。 + +Explanations of related methods and parameters are as follows: +* `create_model`speech synthesis vocoder (using `pwgan_csmsc` as an example), details are as follows: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterParameter DescriptionTypeOptionsDefault Value
model_nameThe name of the modelstrpwgan_csmscNone
model_dirThe storage path of the modelstrNoneNone
deviceModel inference devicestrSupports specifying specific GPU card numbers (e.g. "gpu:0"), other hardware card numbers (e.g. "npu:0"), or CPU (e.g. "cpu")gpu:0
use_hpipWhether to enable high-performance inference plugin. Currently not supported.boolNoneFalse
hpi_configHigh-performance inference configuration. Currently not supported.dict | NoneNoneNone
+ +* Among these, `model_name` must be specified. After specifying `model_name`, PaddleX's built-in model parameters are used by default. When `model_dir` is specified, the user-defined model is used. +* Call the multilingual speech synthesis model's `predict()` method for inference prediction. The `predict()` method parameters include `input` and `batch_size`, with details as follows: + + + + + + + + + + + + + + + + + + + + + + + + + +
ParameterParameter DescriptionTypeOptionsDefault
inputData to be predicted<str +
    + Input mel spectrogram, can be npy file path or tensor type. +
+
None
batch_sizeBatch sizeintCurrently only 1 is supported1
+ +* Process the prediction results. The prediction result for each sample is a corresponding Result object, which supports saving as `json` files: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MethodMethod DescriptionParameterTypeParameter DescriptionDefault
print()Print results to terminalformat_jsonboolWhether to format the output content with JSON indentationTrue
indentintSpecifies the indentation level to beautify output JSON data for better readability. Only effective when format_json is True4
ensure_asciiboolControls whether to escape non-ASCII characters to Unicode. When set to True, all non-ASCII characters will be escaped; False preserves original characters. Only effective when format_json is TrueFalse
save_to_json()Save results as json filesave_pathstrFile save path. When it is a directory, the file name follows the input file type naming conventionNone
indentintSpecifies the indentation level to beautify output JSON data for better readability. Only effective when format_json is True/td> +4
ensure_asciiboolControls whether to escape non-ASCII characters to Unicode. When set to True, all non-ASCII characters will be escaped; False preserves original characters. Only effective when format_json is TrueFalse
save_to_audio()Save results as wav format filesave_pathstrFile save path. When it is a directory, the file name follows the input file type naming conventionNone
+ +* Additionally, the prediction results can also be obtained through attributes: + + + + + + + + + + + + + +
AttributeAttribute Description
jsonGet prediction results in json format
+ +For more information on using PaddleX's single-model inference APIs, please refer to the [PaddleX Single-Model Python Script Usage Instructions](../../instructions/model_python_API.en.md). \ No newline at end of file diff --git a/paddlex/configs/modules/text_to_speech_acoustic/fastspeech2_csmsc.yaml b/paddlex/configs/modules/text_to_speech_acoustic/fastspeech2_csmsc.yaml index 65e5067892..3b539d13a5 100644 --- a/paddlex/configs/modules/text_to_speech_acoustic/fastspeech2_csmsc.yaml +++ b/paddlex/configs/modules/text_to_speech_acoustic/fastspeech2_csmsc.yaml @@ -15,7 +15,7 @@ Global: Predict: batch_size: 1 model_dir: "fastspeech2csmsc" - input: "今天天气真不错" + input: [151, 120, 182, 82, 182, 82, 174, 75, 262, 51, 37, 186, 38, 233] lang: zh speaker_id: 0 kernel_option: diff --git a/paddlex/configs/modules/text_to_speech_vocoder/pwgan_csmsc.yaml b/paddlex/configs/modules/text_to_speech_vocoder/pwgan_csmsc.yaml index 16debe45f0..54b8cde43f 100644 --- a/paddlex/configs/modules/text_to_speech_vocoder/pwgan_csmsc.yaml +++ b/paddlex/configs/modules/text_to_speech_vocoder/pwgan_csmsc.yaml @@ -15,7 +15,7 @@ Global: Predict: batch_size: 1 model_dir: "pwgan_csmsc" - input: "今天天气真不错" + input: "./mel.npy" lang: zh speaker_id: 0 kernel_option: diff --git a/paddlex/inference/models/text_to_speech_vocoder/predictor.py b/paddlex/inference/models/text_to_speech_vocoder/predictor.py index b55f1e2d0c..faf5613f20 100644 --- a/paddlex/inference/models/text_to_speech_vocoder/predictor.py +++ b/paddlex/inference/models/text_to_speech_vocoder/predictor.py @@ -61,7 +61,11 @@ def process(self, batch_data): Returns: dict: A dictionary containing the input path and result. The result include the output pinyin dict. """ - mel = batch_data[0] + input_data = batch_data[0] + if type(input_data) is str: + mel = np.load(input_data) + else: + mel = input_data wav = self.infer([mel]) result = np.array(wav).reshape(1,-1) return { From b0888d854c92b69dd8eeaffb1d7c7968a9b1314e Mon Sep 17 00:00:00 2001 From: supotato6 <1322749946@qq.com> Date: Wed, 16 Jul 2025 02:35:04 +0000 Subject: [PATCH 04/21] change filename --- .../{text_to_pinyin.en.md.md => text_to_pinyin.en.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/module_usage/tutorials/speech_modules/{text_to_pinyin.en.md.md => text_to_pinyin.en.md} (100%) diff --git a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md.md b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md similarity index 100% rename from docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md.md rename to docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md From 2425bc77d48b0814fcce1750dae2e2015168c186 Mon Sep 17 00:00:00 2001 From: supotato6 <1322749946@qq.com> Date: Mon, 21 Jul 2025 02:41:14 +0000 Subject: [PATCH 05/21] model file link --- paddlex/inference/common/result/base_audio_result.py | 2 +- paddlex/inference/utils/official_models.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/paddlex/inference/common/result/base_audio_result.py b/paddlex/inference/common/result/base_audio_result.py index 2c07f86304..71db8e7d43 100644 --- a/paddlex/inference/common/result/base_audio_result.py +++ b/paddlex/inference/common/result/base_audio_result.py @@ -33,4 +33,4 @@ def __init__(self, data: dict) -> None: """ super().__init__(data) - AudioMixin.__init__(self,'wav') + AudioMixin.__init__(self, "wav") diff --git a/paddlex/inference/utils/official_models.py b/paddlex/inference/utils/official_models.py index e111b9fe77..afc78d6000 100644 --- a/paddlex/inference/utils/official_models.py +++ b/paddlex/inference/utils/official_models.py @@ -364,9 +364,9 @@ "PP-DocBee2-3B": "https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0.0/PP-DocBee2-3B_infer.tar", "latin_PP-OCRv5_mobile_rec": "https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0.0/latin_PP-OCRv5_mobile_rec_infer.tar", "korean_PP-OCRv5_mobile_rec": "https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0.0/korean_PP-OCRv5_mobile_rec_infer.tar", - "G2PWModel": "https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0rc0/G2PWModel_1.1.tar", - "fastspeech2_csmsc": "https://paddlespeech.bj.bcebos.com/tmp/fastspeech2_csmsc.tar", - "pwgan_csmsc": "https://paddlespeech.bj.bcebos.com/tmp/pwgan_csmsc.tar", + "G2PWModel": "https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0rc0/G2PWModel.tar", + "fastspeech2_csmsc": "https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0rc0/fastspeech2_csmsc.tar", + "pwgan_csmsc": "https://paddle-model-ecology.bj.bcebos.com/paddlex/official_inference_model/paddle3.0rc0/pwgan_csmsc.tar", } From fd105985b731ba58b8685e9d1141c16d800a3482 Mon Sep 17 00:00:00 2001 From: supotato6 <1322749946@qq.com> Date: Mon, 21 Jul 2025 03:14:06 +0000 Subject: [PATCH 06/21] add module file --- .../text_to_speech_acoustic/evaluator.py | 27 +++++++++++++ .../text_to_speech_acoustic/exportor.py | 28 +++++++++++++ .../text_to_speech_acoustic/trainer.py | 40 +++++++++++++++++++ .../text_to_speech_vocoder/evaluator.py | 27 +++++++++++++ .../text_to_speech_vocoder/exportor.py | 27 +++++++++++++ .../modules/text_to_speech_vocoder/trainer.py | 40 +++++++++++++++++++ 6 files changed, 189 insertions(+) create mode 100644 paddlex/modules/text_to_speech_acoustic/evaluator.py create mode 100644 paddlex/modules/text_to_speech_acoustic/exportor.py create mode 100644 paddlex/modules/text_to_speech_acoustic/trainer.py create mode 100644 paddlex/modules/text_to_speech_vocoder/evaluator.py create mode 100644 paddlex/modules/text_to_speech_vocoder/exportor.py create mode 100644 paddlex/modules/text_to_speech_vocoder/trainer.py diff --git a/paddlex/modules/text_to_speech_acoustic/evaluator.py b/paddlex/modules/text_to_speech_acoustic/evaluator.py new file mode 100644 index 0000000000..d218fecf24 --- /dev/null +++ b/paddlex/modules/text_to_speech_acoustic/evaluator.py @@ -0,0 +1,27 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .model_list import MODELS +from ..base import BaseEvaluator +from ...utils.errors import UnsupportedAPIError + + +class TextToSpeechAcousticEvaluator(BaseEvaluator): + """Instance Fastspeech2Model Model Evaluator""" + + entities = MODELS + + def __init__(self, config): + # not support for now + raise UnsupportedAPIError("Fastspeech2Model model not support evaluate for now.") \ No newline at end of file diff --git a/paddlex/modules/text_to_speech_acoustic/exportor.py b/paddlex/modules/text_to_speech_acoustic/exportor.py new file mode 100644 index 0000000000..15d7f3807d --- /dev/null +++ b/paddlex/modules/text_to_speech_acoustic/exportor.py @@ -0,0 +1,28 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .model_list import MODELS +from ..base import BaseExportor +from ...utils.errors import UnsupportedAPIError + + +class TextToSpeechAcousticExportor(BaseExportor): + """Instance Fastspeech2Model Model Exportor""" + + entities = MODELS + + def __init__(self, config): + """ + # not support for now + raise UnsupportedAPIError("Fastspeech2Model model not support export for now.") \ No newline at end of file diff --git a/paddlex/modules/text_to_speech_acoustic/trainer.py b/paddlex/modules/text_to_speech_acoustic/trainer.py new file mode 100644 index 0000000000..40ef5f26f6 --- /dev/null +++ b/paddlex/modules/text_to_speech_acoustic/trainer.py @@ -0,0 +1,40 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .model_list import MODELS +from ..base import BaseTrainer +from ...utils.errors import UnsupportedAPIError + + +class TextToSpeechAcousticTrainer(BaseTrainer): + """Instance Fastspeech2Model Model Trainer""" + + entities = MODELS + + def __init__(self, config): + # not support for now + raise UnsupportedAPIError("Fastspeech2Model model not support train for now.") + + def update_config(self): + """update training config""" + pass + + def get_train_kwargs(self) -> dict: + """get key-value arguments of model training function + + Returns: + dict: the arguments of training function. + """ + train_args = {"device": self.get_device()} + return train_args \ No newline at end of file diff --git a/paddlex/modules/text_to_speech_vocoder/evaluator.py b/paddlex/modules/text_to_speech_vocoder/evaluator.py new file mode 100644 index 0000000000..9731bc5a9b --- /dev/null +++ b/paddlex/modules/text_to_speech_vocoder/evaluator.py @@ -0,0 +1,27 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .model_list import MODELS +from ..base import BaseEvaluator +from ...utils.errors import UnsupportedAPIError + + +class TextToPinyinEvaluator(BaseEvaluator): + """Instance G2PWModel Model Evaluator""" + + entities = MODELS + + def __init__(self, config): + # not support for now + raise UnsupportedAPIError("G2PWModel model not support evaluate for now.") \ No newline at end of file diff --git a/paddlex/modules/text_to_speech_vocoder/exportor.py b/paddlex/modules/text_to_speech_vocoder/exportor.py new file mode 100644 index 0000000000..3b0b45ade7 --- /dev/null +++ b/paddlex/modules/text_to_speech_vocoder/exportor.py @@ -0,0 +1,27 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .model_list import MODELS +from ..base import BaseExportor +from ...utils.errors import UnsupportedAPIError + + +class TextToPinyinExportor(BaseExportor): + """Instance G2PWModel Model Exportor""" + + entities = MODELS + + def __init__(self, config): + # not support for now + raise UnsupportedAPIError("G2PWModel model not support export for now.") \ No newline at end of file diff --git a/paddlex/modules/text_to_speech_vocoder/trainer.py b/paddlex/modules/text_to_speech_vocoder/trainer.py new file mode 100644 index 0000000000..b72dcf4ffc --- /dev/null +++ b/paddlex/modules/text_to_speech_vocoder/trainer.py @@ -0,0 +1,40 @@ +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .model_list import MODELS +from ..base import BaseTrainer +from ...utils.errors import UnsupportedAPIError + + +class TextToPinyinTrainer(BaseTrainer): + """Instance G2PWModel Model Trainer""" + + entities = MODELS + + def __init__(self, config): + # not support for now + raise UnsupportedAPIError("G2PWModel model not support train for now.") + + def update_config(self): + """update training config""" + pass + + def get_train_kwargs(self) -> dict: + """get key-value arguments of model training function + + Returns: + dict: the arguments of training function. + """ + train_args = {"device": self.get_device()} + return train_args \ No newline at end of file From d1ccc0069a0b717cdea659266b92aa95a9e35b44 Mon Sep 17 00:00:00 2001 From: supotato6 <1322749946@qq.com> Date: Mon, 21 Jul 2025 03:21:33 +0000 Subject: [PATCH 07/21] fix readme link --- .../tutorials/speech_modules/text_to_pinyin.en.md | 2 +- docs/module_usage/tutorials/speech_modules/text_to_pinyin.md | 2 +- .../tutorials/speech_modules/text_to_speech_acoustic.md | 2 +- .../tutorials/speech_modules/text_to_speech_acoustic_en.md | 2 +- .../tutorials/speech_modules/text_to_speech_vocoder.md | 4 ++-- .../tutorials/speech_modules/text_to_speech_vocoder_en.md | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md index 2edde231b8..f079612617 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md @@ -18,7 +18,7 @@ Text to Pinyin is commonly used in the frontend of TTS to convert input Chinese G2PWModel - G2PWModel + G2PWModel 606M g2pW is an open-source text to pinyin model, commonly used in the frontend of TTS. It converts input Chinese text into a tonal Pinyin sequence, providing pronunciation basis for subsequent acoustic models and audio generation diff --git a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md index 1141c45c30..8f50d2720c 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md @@ -18,7 +18,7 @@ comments: true G2PWModel - G2PWModel + G2PWModel 606M g2pW 开源的文本到拼音模型,常用于语音合成的前端,将输入的中文文本转换为带声调的拼音序列,为后续的声学模型和模型生成提供发音依据 diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.md index a682ad5425..caf27e0402 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.md @@ -20,7 +20,7 @@ comments: true fastspeech2_csmsc - fastspeech2_csmsc + fastspeech2_csmsc \\ 157M FastSpeech2 是微软开发的端到端文本转语音(TTS)模型,具备高效稳定的韵律控制能力。它采用非自回归架构,能实现快速高质量的语音合成,适用于虚拟助手、有声读物等多种场景。 diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic_en.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic_en.md index 8ab7678cc7..a9d25f73e8 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic_en.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic_en.md @@ -20,7 +20,7 @@ The acoustic model for speech synthesis is the core component of speech synthesi fastspeech2_csmsc - fastspeech2_csmsc + fastspeech2_csmsc \\ 157M FastSpeech2 is an end-to-end text-to-speech (TTS) model developed by Microsoft, featuring efficient and stable prosody control. It adopts a non-autoregressive architecture that enables fast and high-quality speech synthesis, suitable for various scenarios such as virtual assistants and audiobooks. diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.md index 9ed5b534f2..82c6cb97a5 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.md @@ -21,7 +21,7 @@ comments: true pwgan_csmsc - pwgan_csmsc + pwgan_csmsc \\ 5.1M Parallel WaveGAN(PWGAN)是日本名古屋大学开发的端到端语音合成声码器,具备高效稳定的波形生成能力。它采用生成对抗网络架构,能实现快速高保真的语音重建,适用于TTS系统后端处理、语音增强等多种场景。 @@ -30,7 +30,7 @@ comments: true ## 三、快速集成 在快速集成前,首先需要安装 PaddleX 的 wheel 包,wheel的安装方式请参考[PaddleX本地安装教程](../../../installation/installation.md)。完成 wheel 包的安装后,几行代码即可完成多语种语音合成声学模块的推理,可以任意切换该模块下的模型,您也可以将多语种语音合成模块中的模型推理集成到您的项目中。 - +运行以下代码前,请您下载[示例npy](https://paddlespeech.bj.bcebos.com/demos/paddlex/mel.npy)到本地。 ```python from paddlex import create_model diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder_en.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder_en.md index 14c3041577..c414d57931 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder_en.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder_en.md @@ -20,7 +20,7 @@ The speech synthesis vocoder is a key component of speech synthesis technology. pwgan_csmsc - pwgan_csmsc + pwgan_csmsc \\ 5.1M Parallel WaveGAN (PWGAN) is an end-to-end speech synthesis vocoder developed by Nagoya University, Japan, featuring efficient and stable waveform generation capabilities. It adopts a generative adversarial network architecture that enables fast and high-fidelity speech reconstruction, suitable for various scenarios such as TTS system backend processing and speech enhancement. From 4b5ed89062167d8914921b7f4cdd216d505de82c Mon Sep 17 00:00:00 2001 From: supotato6 <1322749946@qq.com> Date: Mon, 21 Jul 2025 03:22:56 +0000 Subject: [PATCH 08/21] filename --- ...ext_to_speech_acoustic_en.md => text_to_speech_acoustic.en.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/module_usage/tutorials/speech_modules/{text_to_speech_acoustic_en.md => text_to_speech_acoustic.en.md} (100%) diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic_en.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.en.md similarity index 100% rename from docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic_en.md rename to docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.en.md From b34303c899d3c4b11097c03ae6e483a22ed08d31 Mon Sep 17 00:00:00 2001 From: supotato6 <1322749946@qq.com> Date: Mon, 21 Jul 2025 03:23:49 +0000 Subject: [PATCH 09/21] filename1 --- ...{text_to_speech_vocoder_en.md => text_to_speech_vocoder.en.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/module_usage/tutorials/speech_modules/{text_to_speech_vocoder_en.md => text_to_speech_vocoder.en.md} (100%) diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder_en.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.en.md similarity index 100% rename from docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder_en.md rename to docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.en.md From 10db1c2f860426bcc954188be51cc1703252d247 Mon Sep 17 00:00:00 2001 From: supotato6 <1322749946@qq.com> Date: Mon, 21 Jul 2025 03:31:23 +0000 Subject: [PATCH 10/21] module methods --- .../tutorials/speech_modules/text_to_speech_vocoder.en.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.en.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.en.md index c414d57931..d36ff6de5f 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.en.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.en.md @@ -29,7 +29,7 @@ The speech synthesis vocoder is a key component of speech synthesis technology. ## III. Quick Integration Before quick integration, first install the PaddleX wheel package. For wheel installation methods, please refer to [PaddleX Local Installation Tutorial](../../../installation/installation.md). After installing the wheel package, inference for the multilingual speech synthesis acoustic module can be completed with just a few lines of code. You can freely switch models within this module, or integrate model inference from the multilingual speech synthesis module into your project. - +Before running the following code, please download the [sample npy](https://paddlespeech.bj.bcebos.com/demos/paddlex/mel.npy){target="_blank"} to your local machine. ```python from paddlex import create_model From 98972c3786b5e2da9d79d9d25d81d2c502fe4bc0 Mon Sep 17 00:00:00 2001 From: supotato6 <1322749946@qq.com> Date: Mon, 21 Jul 2025 03:31:58 +0000 Subject: [PATCH 11/21] module methods init --- paddlex/modules/text_to_speech_acoustic/__init__.py | 6 +++--- paddlex/modules/text_to_speech_vocoder/__init__.py | 6 +++--- paddlex/modules/text_to_speech_vocoder/evaluator.py | 6 +++--- paddlex/modules/text_to_speech_vocoder/exportor.py | 6 +++--- paddlex/modules/text_to_speech_vocoder/trainer.py | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/paddlex/modules/text_to_speech_acoustic/__init__.py b/paddlex/modules/text_to_speech_acoustic/__init__.py index d5b01de2e7..b46c861be6 100644 --- a/paddlex/modules/text_to_speech_acoustic/__init__.py +++ b/paddlex/modules/text_to_speech_acoustic/__init__.py @@ -13,6 +13,6 @@ # limitations under the License. from .dataset_checker import TextToSpeechAcousticDatasetChecker -# from .trainer import TextToSpeechTrainer -# from .evaluator import TextToSpeechEvaluator -# from .exportor import TextToSpeechExportor +from .trainer import TextToSpeechAcousticTrainer +from .evaluator import TextToSpeechAcousticEvaluator +from .exportor import TextToSpeechAcousticExportor diff --git a/paddlex/modules/text_to_speech_vocoder/__init__.py b/paddlex/modules/text_to_speech_vocoder/__init__.py index 8575483b15..35bb987980 100644 --- a/paddlex/modules/text_to_speech_vocoder/__init__.py +++ b/paddlex/modules/text_to_speech_vocoder/__init__.py @@ -13,6 +13,6 @@ # limitations under the License. from .dataset_checker import TextToSpeechVocoderDatasetChecker -# from .trainer import TextToSpeechTrainer -# from .evaluator import TextToSpeechEvaluator -# from .exportor import TextToSpeechExportor +from .trainer import TextToSpeechVocoderTrainer +from .evaluator import TextToSpeechVocoderEvaluator +from .exportor import TextToSpeechVocoderExportor diff --git a/paddlex/modules/text_to_speech_vocoder/evaluator.py b/paddlex/modules/text_to_speech_vocoder/evaluator.py index 9731bc5a9b..19131c875d 100644 --- a/paddlex/modules/text_to_speech_vocoder/evaluator.py +++ b/paddlex/modules/text_to_speech_vocoder/evaluator.py @@ -17,11 +17,11 @@ from ...utils.errors import UnsupportedAPIError -class TextToPinyinEvaluator(BaseEvaluator): - """Instance G2PWModel Model Evaluator""" +class TextToSpeechVocoderEvaluator(BaseEvaluator): + """Instance PWGan Model Model Evaluator""" entities = MODELS def __init__(self, config): # not support for now - raise UnsupportedAPIError("G2PWModel model not support evaluate for now.") \ No newline at end of file + raise UnsupportedAPIError("PWGan model not support evaluate for now.") \ No newline at end of file diff --git a/paddlex/modules/text_to_speech_vocoder/exportor.py b/paddlex/modules/text_to_speech_vocoder/exportor.py index 3b0b45ade7..59097e2777 100644 --- a/paddlex/modules/text_to_speech_vocoder/exportor.py +++ b/paddlex/modules/text_to_speech_vocoder/exportor.py @@ -17,11 +17,11 @@ from ...utils.errors import UnsupportedAPIError -class TextToPinyinExportor(BaseExportor): - """Instance G2PWModel Model Exportor""" +class TextToSpeechVocoderExportor(BaseExportor): + """Instance PWGanModel Model Exportor""" entities = MODELS def __init__(self, config): # not support for now - raise UnsupportedAPIError("G2PWModel model not support export for now.") \ No newline at end of file + raise UnsupportedAPIError("PWGanModel model not support export for now.") \ No newline at end of file diff --git a/paddlex/modules/text_to_speech_vocoder/trainer.py b/paddlex/modules/text_to_speech_vocoder/trainer.py index b72dcf4ffc..6e509e1709 100644 --- a/paddlex/modules/text_to_speech_vocoder/trainer.py +++ b/paddlex/modules/text_to_speech_vocoder/trainer.py @@ -17,14 +17,14 @@ from ...utils.errors import UnsupportedAPIError -class TextToPinyinTrainer(BaseTrainer): - """Instance G2PWModel Model Trainer""" +class TextToSpeechVocoderTrainer(BaseTrainer): + """Instance PWGanModel Model Trainer""" entities = MODELS def __init__(self, config): # not support for now - raise UnsupportedAPIError("G2PWModel model not support train for now.") + raise UnsupportedAPIError("PWGanModel model not support train for now.") def update_config(self): """update training config""" From 7c1cfa859074d378d42a91d45c37439afe3a9473 Mon Sep 17 00:00:00 2001 From: supotato6 <1322749946@qq.com> Date: Wed, 6 Aug 2025 09:35:15 +0000 Subject: [PATCH 12/21] fix file --- .../inference/models/text_to_speech_acoustic/predictor.py | 8 ++------ .../models/text_to_speech_acoustic/processors.py | 1 - .../inference/models/text_to_speech_acoustic/result.py | 3 ++- .../inference/models/text_to_speech_vocoder/predictor.py | 8 +++----- .../inference/models/text_to_speech_vocoder/processors.py | 1 - paddlex/inference/models/text_to_speech_vocoder/result.py | 3 ++- 6 files changed, 9 insertions(+), 15 deletions(-) diff --git a/paddlex/inference/models/text_to_speech_acoustic/predictor.py b/paddlex/inference/models/text_to_speech_acoustic/predictor.py index d2a159c5e8..a2ba95d6b0 100644 --- a/paddlex/inference/models/text_to_speech_acoustic/predictor.py +++ b/paddlex/inference/models/text_to_speech_acoustic/predictor.py @@ -12,14 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import numpy as np - +from ....modules.text_to_speech_acoustic.model_list import MODELS from ...common.batch_sampler import AudioBatchSampler - from ..base import BasePredictor from .result import Fastspeech2Result -from ....modules.text_to_speech_acoustic.model_list import MODELS - class Fastspeech2Predictor(BasePredictor): @@ -35,6 +31,7 @@ def __init__(self, *args, **kwargs): """ super().__init__(*args, **kwargs) self.infer = self.create_static_infer() + def _build_batch_sampler(self): """Builds and returns an AudioBatchSampler instance. @@ -66,4 +63,3 @@ def process(self, batch_data): return { "result": mel, } - diff --git a/paddlex/inference/models/text_to_speech_acoustic/processors.py b/paddlex/inference/models/text_to_speech_acoustic/processors.py index 686923cae1..daf6e239d9 100644 --- a/paddlex/inference/models/text_to_speech_acoustic/processors.py +++ b/paddlex/inference/models/text_to_speech_acoustic/processors.py @@ -11,4 +11,3 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - diff --git a/paddlex/inference/models/text_to_speech_acoustic/result.py b/paddlex/inference/models/text_to_speech_acoustic/result.py index 8878e5bb5c..2dbd5b0a48 100644 --- a/paddlex/inference/models/text_to_speech_acoustic/result.py +++ b/paddlex/inference/models/text_to_speech_acoustic/result.py @@ -15,7 +15,8 @@ from ...common.result import BaseResult + class Fastspeech2Result(BaseResult): def __init__(self, data: dict) -> None: - super().__init__(data) \ No newline at end of file + super().__init__(data) diff --git a/paddlex/inference/models/text_to_speech_vocoder/predictor.py b/paddlex/inference/models/text_to_speech_vocoder/predictor.py index faf5613f20..65ed91e853 100644 --- a/paddlex/inference/models/text_to_speech_vocoder/predictor.py +++ b/paddlex/inference/models/text_to_speech_vocoder/predictor.py @@ -14,12 +14,10 @@ import numpy as np -from ....utils.func_register import FuncRegister +from ....modules.text_to_speech_vocoder.model_list import MODELS from ...common.batch_sampler import AudioBatchSampler - from ..base import BasePredictor from .result import PwganResult -from ....modules.text_to_speech_vocoder.model_list import MODELS class PwganPredictor(BasePredictor): @@ -35,6 +33,7 @@ def __init__(self, *args, **kwargs): """ super().__init__(*args, **kwargs) self.infer = self.create_static_infer() + def _build_batch_sampler(self): """Builds and returns an AudioBatchSampler instance. @@ -67,8 +66,7 @@ def process(self, batch_data): else: mel = input_data wav = self.infer([mel]) - result = np.array(wav).reshape(1,-1) + result = np.array(wav).reshape(1, -1) return { "result": result, } - diff --git a/paddlex/inference/models/text_to_speech_vocoder/processors.py b/paddlex/inference/models/text_to_speech_vocoder/processors.py index 686923cae1..daf6e239d9 100644 --- a/paddlex/inference/models/text_to_speech_vocoder/processors.py +++ b/paddlex/inference/models/text_to_speech_vocoder/processors.py @@ -11,4 +11,3 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - diff --git a/paddlex/inference/models/text_to_speech_vocoder/result.py b/paddlex/inference/models/text_to_speech_vocoder/result.py index 5f4c7ab64c..b5929c8cbc 100644 --- a/paddlex/inference/models/text_to_speech_vocoder/result.py +++ b/paddlex/inference/models/text_to_speech_vocoder/result.py @@ -19,5 +19,6 @@ class PwganResult(BaseAudioResult): def __init__(self, data: dict) -> None: super().__init__(data) + def _to_audio(self): - return {"res": self} \ No newline at end of file + return {"res": self} From bd74e33e5e120ef4ef012b2a678e5a7d6b88d515 Mon Sep 17 00:00:00 2001 From: supotato6 Date: Wed, 6 Aug 2025 09:57:52 +0000 Subject: [PATCH 13/21] fix readme --- .../tutorials/speech_modules/text_to_pinyin.en.md | 2 +- .../module_usage/tutorials/speech_modules/text_to_pinyin.md | 2 +- .../tutorials/speech_modules/text_to_speech_vocoder.en.md | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md index f079612617..fe65d4cbb0 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md @@ -179,4 +179,4 @@ Related methods, parameters, and explanations are as follows: -For more information on using PaddleX's single-model inference APIs, please refer to the [PaddleX Single-Model Python Script Usage Instructions](../../instructions/model_python_API.en.md). \ No newline at end of file +For more information on using PaddleX's single-model inference APIs, please refer to the [PaddleX Single-Model Python Script Usage Instructions](../../instructions/model_python_API.en.md). diff --git a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md index 8f50d2720c..64cf461bad 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md @@ -176,4 +176,4 @@ for res in output: -关于更多 PaddleX 的单模型推理的 API 的使用方法,可以参考[PaddleX单模型Python脚本使用说明](../../instructions/model_python_API.md)。 \ No newline at end of file +关于更多 PaddleX 的单模型推理的 API 的使用方法,可以参考[PaddleX单模型Python脚本使用说明](../../instructions/model_python_API.md)。 diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.en.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.en.md index d36ff6de5f..219ab57113 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.en.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.en.md @@ -29,7 +29,7 @@ The speech synthesis vocoder is a key component of speech synthesis technology. ## III. Quick Integration Before quick integration, first install the PaddleX wheel package. For wheel installation methods, please refer to [PaddleX Local Installation Tutorial](../../../installation/installation.md). After installing the wheel package, inference for the multilingual speech synthesis acoustic module can be completed with just a few lines of code. You can freely switch models within this module, or integrate model inference from the multilingual speech synthesis module into your project. -Before running the following code, please download the [sample npy](https://paddlespeech.bj.bcebos.com/demos/paddlex/mel.npy){target="_blank"} to your local machine. +Before running the following code, please download the [sample npy](https://paddlespeech.bj.bcebos.com/demos/paddlex/mel.npy){target="_blank"} to your local machine. ```python from paddlex import create_model @@ -114,7 +114,7 @@ Explanations of related methods and parameters are as follows: str
    - Input mel spectrogram, can be npy file path or tensor type. + Input mel spectrogram, can be npy file path or tensor type.
None @@ -206,4 +206,4 @@ Explanations of related methods and parameters are as follows: -For more information on using PaddleX's single-model inference APIs, please refer to the [PaddleX Single-Model Python Script Usage Instructions](../../instructions/model_python_API.en.md). \ No newline at end of file +For more information on using PaddleX's single-model inference APIs, please refer to the [PaddleX Single-Model Python Script Usage Instructions](../../instructions/model_python_API.en.md). From 709c9f8f41e8f52064fcab27943d4d4b5d8ce083 Mon Sep 17 00:00:00 2001 From: supotato6 Date: Wed, 20 Aug 2025 11:48:16 +0800 Subject: [PATCH 14/21] rm useless file --- .../models/text_to_speech_acoustic/processors.py | 13 ------------- .../models/text_to_speech_vocoder/processors.py | 13 ------------- 2 files changed, 26 deletions(-) delete mode 100644 paddlex/inference/models/text_to_speech_acoustic/processors.py delete mode 100644 paddlex/inference/models/text_to_speech_vocoder/processors.py diff --git a/paddlex/inference/models/text_to_speech_acoustic/processors.py b/paddlex/inference/models/text_to_speech_acoustic/processors.py deleted file mode 100644 index daf6e239d9..0000000000 --- a/paddlex/inference/models/text_to_speech_acoustic/processors.py +++ /dev/null @@ -1,13 +0,0 @@ -# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/paddlex/inference/models/text_to_speech_vocoder/processors.py b/paddlex/inference/models/text_to_speech_vocoder/processors.py deleted file mode 100644 index daf6e239d9..0000000000 --- a/paddlex/inference/models/text_to_speech_vocoder/processors.py +++ /dev/null @@ -1,13 +0,0 @@ -# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. From 4bdd7bdb6fd3328bed94b4c440e741b1ce03f5e0 Mon Sep 17 00:00:00 2001 From: supotato6 Date: Wed, 20 Aug 2025 11:51:57 +0800 Subject: [PATCH 15/21] fix information --- paddlex/modules/text_to_speech_acoustic/__init__.py | 2 +- paddlex/modules/text_to_speech_acoustic/dataset_checker.py | 2 +- paddlex/modules/text_to_speech_vocoder/__init__.py | 2 +- paddlex/modules/text_to_speech_vocoder/dataset_checker.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/paddlex/modules/text_to_speech_acoustic/__init__.py b/paddlex/modules/text_to_speech_acoustic/__init__.py index b46c861be6..900a679ec0 100644 --- a/paddlex/modules/text_to_speech_acoustic/__init__.py +++ b/paddlex/modules/text_to_speech_acoustic/__init__.py @@ -1,4 +1,4 @@ -# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/paddlex/modules/text_to_speech_acoustic/dataset_checker.py b/paddlex/modules/text_to_speech_acoustic/dataset_checker.py index 83521e3ae8..9de7fded05 100644 --- a/paddlex/modules/text_to_speech_acoustic/dataset_checker.py +++ b/paddlex/modules/text_to_speech_acoustic/dataset_checker.py @@ -1,4 +1,4 @@ -# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/paddlex/modules/text_to_speech_vocoder/__init__.py b/paddlex/modules/text_to_speech_vocoder/__init__.py index 35bb987980..a668ef3169 100644 --- a/paddlex/modules/text_to_speech_vocoder/__init__.py +++ b/paddlex/modules/text_to_speech_vocoder/__init__.py @@ -1,4 +1,4 @@ -# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/paddlex/modules/text_to_speech_vocoder/dataset_checker.py b/paddlex/modules/text_to_speech_vocoder/dataset_checker.py index e2d7fd3b62..f766c55c1b 100644 --- a/paddlex/modules/text_to_speech_vocoder/dataset_checker.py +++ b/paddlex/modules/text_to_speech_vocoder/dataset_checker.py @@ -1,4 +1,4 @@ -# copyright (c) 2024 PaddlePaddle Authors. All Rights Reserve. +# copyright (c) 2025 PaddlePaddle Authors. All Rights Reserve. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From d1c036744035d269718e31967292c54fdeffdd6d Mon Sep 17 00:00:00 2001 From: supotato6 Date: Wed, 20 Aug 2025 15:37:34 +0800 Subject: [PATCH 16/21] fix comments --- paddlex/modules/__init__.py | 14 ++++++++++++-- .../modules/text_to_speech_acoustic/evaluator.py | 4 ++-- .../modules/text_to_speech_acoustic/exportor.py | 1 - 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/paddlex/modules/__init__.py b/paddlex/modules/__init__.py index 5e00c202c3..c99e8c05b1 100644 --- a/paddlex/modules/__init__.py +++ b/paddlex/modules/__init__.py @@ -88,8 +88,18 @@ TextRecExportor, TextRecTrainer, ) -from .text_to_speech_vocoder import TextToSpeechVocoderDatasetChecker -from .text_to_speech_acoustic import TextToSpeechAcousticDatasetChecker +from .text_to_speech_vocoder import ( + TextToSpeechVocoderDatasetChecker + TextToSpeechVocoderEvaluator, + TextToSpeechVocoderExportor, + TextToSpeechVocoderTrainer, +) +from .text_to_speech_acoustic import ( + TextToSpeechAcousticDatasetChecker + TextToSpeechAcousticEvaluator, + TextToSpeechAcousticExportor, + TextToSpeechAcousticTrainer, +) from .text_to_pinyin import ( TextToPinyinDatasetChecker, TextToPinyinEvaluator, diff --git a/paddlex/modules/text_to_speech_acoustic/evaluator.py b/paddlex/modules/text_to_speech_acoustic/evaluator.py index d218fecf24..5e8399fe6d 100644 --- a/paddlex/modules/text_to_speech_acoustic/evaluator.py +++ b/paddlex/modules/text_to_speech_acoustic/evaluator.py @@ -18,10 +18,10 @@ class TextToSpeechAcousticEvaluator(BaseEvaluator): - """Instance Fastspeech2Model Model Evaluator""" + """Instance Fastspeech2 Model Evaluator""" entities = MODELS def __init__(self, config): # not support for now - raise UnsupportedAPIError("Fastspeech2Model model not support evaluate for now.") \ No newline at end of file + raise UnsupportedAPIError("Fastspeech2 model not support evaluate for now.") \ No newline at end of file diff --git a/paddlex/modules/text_to_speech_acoustic/exportor.py b/paddlex/modules/text_to_speech_acoustic/exportor.py index 15d7f3807d..1cf3c597c0 100644 --- a/paddlex/modules/text_to_speech_acoustic/exportor.py +++ b/paddlex/modules/text_to_speech_acoustic/exportor.py @@ -23,6 +23,5 @@ class TextToSpeechAcousticExportor(BaseExportor): entities = MODELS def __init__(self, config): - """ # not support for now raise UnsupportedAPIError("Fastspeech2Model model not support export for now.") \ No newline at end of file From 01dcb8c768c6cc800fcf59a5fd2956f012f30c63 Mon Sep 17 00:00:00 2001 From: supotato6 Date: Fri, 22 Aug 2025 16:58:03 +0800 Subject: [PATCH 17/21] fix bug --- paddlex/inference/models/text_to_pinyin/processors.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/paddlex/inference/models/text_to_pinyin/processors.py b/paddlex/inference/models/text_to_pinyin/processors.py index 9b0e2cb00b..6a16566545 100644 --- a/paddlex/inference/models/text_to_pinyin/processors.py +++ b/paddlex/inference/models/text_to_pinyin/processors.py @@ -723,7 +723,9 @@ def __call__(self, sentences: List[str]) -> List[List[str]]: ) if len(texts) == 0: # sentences no polyphonic words - return partial_results + phones = self._pinyin2p(partial_results[0], sentences) + phone_ids = self._p2id(phones[0]) + return {"phones": phones[0], "phone_ids": phone_ids, "pinyins": partial_results[0]} onnx_input = prepare_onnx_input( tokenizer=self.tokenizer, From d4b4847de81c92bf3d09151fb9a30f6cf419c695 Mon Sep 17 00:00:00 2001 From: supotato6 Date: Wed, 10 Sep 2025 11:44:23 +0800 Subject: [PATCH 18/21] fix comments --- .../text_to_speech_acoustic/fastspeech2_csmsc.yaml | 14 +++++--------- .../text_to_speech_vocoder/pwgan_csmsc.yaml | 10 +++------- paddlex/inference/utils/io/writers.py | 11 ++++++++--- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/paddlex/configs/modules/text_to_speech_acoustic/fastspeech2_csmsc.yaml b/paddlex/configs/modules/text_to_speech_acoustic/fastspeech2_csmsc.yaml index 3b539d13a5..b7a525ecf0 100644 --- a/paddlex/configs/modules/text_to_speech_acoustic/fastspeech2_csmsc.yaml +++ b/paddlex/configs/modules/text_to_speech_acoustic/fastspeech2_csmsc.yaml @@ -1,22 +1,18 @@ Global: model: fastspeech2_csmsc mode: predict # only support predict - device: gpu:0 - use_trt: False - use_mkldnn: False - cpu_threads: 1 - precision: "fp32" output: "output" - model_name: "fastspeech2_csmsc" - speaker_dict: None - lang: zh - speaker_id: 0 Predict: batch_size: 1 + device: gpu:0 model_dir: "fastspeech2csmsc" input: [151, 120, 182, 82, 182, 82, 174, 75, 262, 51, 37, 186, 38, 233] lang: zh speaker_id: 0 + use_trt: False + use_mkldnn: False + cpu_threads: 1 + precision: "fp32" kernel_option: run_mode: paddle \ No newline at end of file diff --git a/paddlex/configs/modules/text_to_speech_vocoder/pwgan_csmsc.yaml b/paddlex/configs/modules/text_to_speech_vocoder/pwgan_csmsc.yaml index 54b8cde43f..681e3c172c 100644 --- a/paddlex/configs/modules/text_to_speech_vocoder/pwgan_csmsc.yaml +++ b/paddlex/configs/modules/text_to_speech_vocoder/pwgan_csmsc.yaml @@ -1,18 +1,14 @@ Global: model: pwgan_csmsc mode: predict # only support predict + output: "output" + +Predict: device: gpu:0 use_trt: False use_mkldnn: False cpu_threads: 1 precision: "fp32" - output: "output" - model_name: "pwgan_csmsc" - speaker_dict: None - lang: zh - speaker_id: 0 - -Predict: batch_size: 1 model_dir: "pwgan_csmsc" input: "./mel.npy" diff --git a/paddlex/inference/utils/io/writers.py b/paddlex/inference/utils/io/writers.py index 71c650ef65..200736896c 100644 --- a/paddlex/inference/utils/io/writers.py +++ b/paddlex/inference/utils/io/writers.py @@ -59,6 +59,7 @@ class WriterType(enum.Enum): TXT = 10 AUDIO = 11 + class _BaseWriter(object): """_BaseWriter""" @@ -261,12 +262,14 @@ def get_type(self): """get type""" return WriterType.MARKDOWN + class AudioWriter(_BaseWriter): """AudioWriter""" def __init__(self, sample_rate=24000, backend="wav", **bk_args): super().__init__(sample_rate=sample_rate, backend=backend, **bk_args) self.sample_rate = sample_rate + def write(self, out_path, obj): """write""" return self._backend.write_obj(str(out_path), obj) @@ -282,6 +285,7 @@ def get_type(self): """get type""" return WriterType.AUDIO + class _BaseWriterBackend(object): """_BaseWriterBackend""" @@ -484,10 +488,11 @@ def _write_obj(self, out_path, obj): class AudioWriterBackend(_BaseWriterBackend): """AudioWriterBackend""" - def __init__(self,sample_rate=24000): + def __init__(self, sample_rate=24000): super().__init__() self.sample_rate = sample_rate + def _write_obj(self, out_path, obj): """write audio obj""" - audio = obj['result'] - sf.write(out_path, audio, self.sample_rate) \ No newline at end of file + audio = obj["result"] + sf.write(out_path, audio, self.sample_rate) From 9ff423077721bc5ba9729b1be9aafeac59e62846 Mon Sep 17 00:00:00 2001 From: supotato6 Date: Thu, 11 Sep 2025 15:12:24 +0800 Subject: [PATCH 19/21] delete code --- .../models/text_to_pinyin/processors.py | 17 ++--------------- paddlex/modules/__init__.py | 4 ++-- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/paddlex/inference/models/text_to_pinyin/processors.py b/paddlex/inference/models/text_to_pinyin/processors.py index 6a16566545..0f9f097263 100644 --- a/paddlex/inference/models/text_to_pinyin/processors.py +++ b/paddlex/inference/models/text_to_pinyin/processors.py @@ -574,6 +574,7 @@ def __init__( self.chars = sorted(list(self.char2phonemes.keys())) + self.with_erhua = False self.polyphonic_chars_new = set(self.chars) for char in self.non_polyphonic: if char in self.polyphonic_chars_new: @@ -614,8 +615,6 @@ def _pinyin2p(self,pinyins: List[str],words: List[str])->List[str]: seg = re.sub('[a-zA-Z]+', '', seg) # [(word, pos), ...] seg_cut = psg.lcut(seg) - # fix wordseg bad case for sandhi - # seg_cut = self.tone_modifier.pre_merge_for_modify(seg_cut) # 为了多音词获得更好的效果,这里采用整句预测 phones = [] initials = [] @@ -633,10 +632,6 @@ def _pinyin2p(self,pinyins: List[str],words: List[str])->List[str]: word_pinyins = pinyins[pre_word_length:now_word_length] - # 多音字消歧 - # word_pinyins = self.corrector.correct_pronunciation( - # word, word_pinyins) - for word_pinyin, char in zip(word_pinyins, word): if word_pinyin is None: word_pinyin = char @@ -658,14 +653,6 @@ def _pinyin2p(self,pinyins: List[str],words: List[str])->List[str]: sub_finals.append(word_pinyin) pre_word_length = now_word_length - # # tone sandhi - # sub_finals = self.tone_modifier.modified_tone(word, pos, - # sub_finals) - # er hua - # if with_erhua: - # sub_initials, sub_finals = self._merge_erhua( - # sub_initials, sub_finals, word, pos) - initials.append(sub_initials) finals.append(sub_finals) initials = sum(initials, []) @@ -773,7 +760,7 @@ def _prepare_data( ) elif char in self.char_bopomofo_dict: partial_result[i] = pypinyin_result[i][0] - # partial_result[i] = self.style_convert_func(self.char_bopomofo_dict[char][0]) + partial_result[i] = self.style_convert_func(self.char_bopomofo_dict[char][0]) else: partial_result[i] = pypinyin_result[i][0] diff --git a/paddlex/modules/__init__.py b/paddlex/modules/__init__.py index c99e8c05b1..0ed9d96fb0 100644 --- a/paddlex/modules/__init__.py +++ b/paddlex/modules/__init__.py @@ -89,13 +89,13 @@ TextRecTrainer, ) from .text_to_speech_vocoder import ( - TextToSpeechVocoderDatasetChecker + TextToSpeechVocoderDatasetChecker, TextToSpeechVocoderEvaluator, TextToSpeechVocoderExportor, TextToSpeechVocoderTrainer, ) from .text_to_speech_acoustic import ( - TextToSpeechAcousticDatasetChecker + TextToSpeechAcousticDatasetChecker, TextToSpeechAcousticEvaluator, TextToSpeechAcousticExportor, TextToSpeechAcousticTrainer, From 29c14e2f1a6ffb1f1aa3604e9b59bc450426dbc6 Mon Sep 17 00:00:00 2001 From: supotato6 Date: Mon, 10 Nov 2025 14:35:26 +0800 Subject: [PATCH 20/21] fix comment --- .../tutorials/speech_modules/text_to_pinyin.en.md | 2 +- .../module_usage/tutorials/speech_modules/text_to_pinyin.md | 2 +- .../tutorials/speech_modules/text_to_speech_acoustic.en.md | 6 ++---- .../tutorials/speech_modules/text_to_speech_acoustic.md | 4 +--- .../tutorials/speech_modules/text_to_speech_vocoder.en.md | 4 +--- .../tutorials/speech_modules/text_to_speech_vocoder.md | 4 +--- 6 files changed, 7 insertions(+), 15 deletions(-) diff --git a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md index fe65d4cbb0..2e4c7c3571 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.en.md @@ -95,7 +95,7 @@ Related methods, parameters, and explanations are as follows: str
    -
  • Input text, such as: 欢迎使用飞桨
  • + Input text, such as: 欢迎使用飞桨
None diff --git a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md index 64cf461bad..2481170342 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_pinyin.md @@ -91,7 +91,7 @@ for res in output: str
    -
  • 对应文本,如:欢迎使用飞桨
  • + 对应文本,如:欢迎使用飞桨
无 diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.en.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.en.md index a9d25f73e8..21c3cbda2f 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.en.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.en.md @@ -21,7 +21,7 @@ The acoustic model for speech synthesis is the core component of speech synthesi fastspeech2_csmsc fastspeech2_csmsc - \\ + \ 157M FastSpeech2 is an end-to-end text-to-speech (TTS) model developed by Microsoft, featuring efficient and stable prosody control. It adopts a non-autoregressive architecture that enables fast and high-quality speech synthesis, suitable for various scenarios such as virtual assistants and audiobooks. @@ -119,9 +119,7 @@ Explanations of related methods and parameters are as follows: Data to be predicted str -
    - Currently only supports tensor-type input_phone_ids, such as [151, 120, 182, 82, 182, 82, 174, 75, 262, 51, 37, 186], etc. -
+Currently only supports tensor-type input_phone_ids, such as [151, 120, 182, 82, 182, 82, 174, 75, 262, 51, 37, 186], etc. None diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.md index caf27e0402..7373d7a06a 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_speech_acoustic.md @@ -21,7 +21,7 @@ comments: true fastspeech2_csmsc fastspeech2_csmsc - \\ + \ 157M FastSpeech2 是微软开发的端到端文本转语音(TTS)模型,具备高效稳定的韵律控制能力。它采用非自回归架构,能实现快速高质量的语音合成,适用于虚拟助手、有声读物等多种场景。 @@ -117,9 +117,7 @@ for res in output: 待预测数据 str -
    输入的input_phone_ids, 目前只支持tensor类型,如[151, 120, 182, 82, 182, 82, 174, 75, 262, 51, 37, 186]等 -
无 diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.en.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.en.md index 219ab57113..18e1177a2d 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.en.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.en.md @@ -21,7 +21,7 @@ The speech synthesis vocoder is a key component of speech synthesis technology. pwgan_csmsc pwgan_csmsc - \\ + \ 5.1M Parallel WaveGAN (PWGAN) is an end-to-end speech synthesis vocoder developed by Nagoya University, Japan, featuring efficient and stable waveform generation capabilities. It adopts a generative adversarial network architecture that enables fast and high-fidelity speech reconstruction, suitable for various scenarios such as TTS system backend processing and speech enhancement. @@ -113,9 +113,7 @@ Explanations of related methods and parameters are as follows: Data to be predicted< str -
    Input mel spectrogram, can be npy file path or tensor type. -
None diff --git a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.md b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.md index 82c6cb97a5..ab89d9f269 100644 --- a/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.md +++ b/docs/module_usage/tutorials/speech_modules/text_to_speech_vocoder.md @@ -22,7 +22,7 @@ comments: true pwgan_csmsc pwgan_csmsc - \\ + \ 5.1M Parallel WaveGAN(PWGAN)是日本名古屋大学开发的端到端语音合成声码器,具备高效稳定的波形生成能力。它采用生成对抗网络架构,能实现快速高保真的语音重建,适用于TTS系统后端处理、语音增强等多种场景。 @@ -115,9 +115,7 @@ for res in output: 待预测数据 str -
    输入的mel谱, 可以是npy文件路径或者tensor类型。 -
无 From 69db8eccc9af94e892fa9bafd0bc81faf651f034 Mon Sep 17 00:00:00 2001 From: supotato6 Date: Mon, 10 Nov 2025 15:43:49 +0800 Subject: [PATCH 21/21] audiomixin init --- paddlex/inference/common/result/mixin.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/paddlex/inference/common/result/mixin.py b/paddlex/inference/common/result/mixin.py index 4eed0b8f1d..fc10b99d5a 100644 --- a/paddlex/inference/common/result/mixin.py +++ b/paddlex/inference/common/result/mixin.py @@ -1073,6 +1073,7 @@ def __init__(self, backend, *args: List, **kwargs: Dict) -> None: """ self._backend = backend self._save_funcs.append(self.save_to_audio) + self._audio_writer = AudioWriter(backend=self._backend, *args, **kwargs) @abstractmethod def _to_audio(self) -> Dict[str, np.array]: @@ -1105,7 +1106,7 @@ def _is_audio_file(file_path): mime_type, _ = mimetypes.guess_type(file_path) return mime_type is not None and mime_type.startswith("audio/") - audio_writer = AudioWriter(backend=self._backend, *args, **kwargs) + audio = self._to_audio() if not _is_audio_file(save_path): fn = Path(self._get_input_fn()) @@ -1114,13 +1115,13 @@ def _is_audio_file(file_path): base_save_path = Path(save_path) for key in audio: save_path = base_save_path / f"{stem}_{key}{suffix}" - audio_writer.write(save_path.as_posix(), audio[key], *args, **kwargs) + self._audio_writer.write(save_path.as_posix(), audio[key], *args, **kwargs) else: if len(audio) > 1: logging.warning( f"The result has multiple audio files need to be saved. But the `save_path` has been specified as `{save_path}`!" ) - audio_writer.write(save_path, audio[list(audio.keys())[0]], *args, **kwargs) + self._audio_writer.write(save_path, audio[list(audio.keys())[0]], *args, **kwargs) class MarkdownMixin: """Mixin class for adding Markdown handling capabilities."""