CoolFace
Apppublic

lenML/ChatTTS-Forge

sourceHugging Faceagpl-3.0updated 2y agoView on Hugging Face
301likes
1# Copyright (c) 2021 PaddlePaddle Authors. All Rights Reserved.2#3# Licensed under the Apache License, Version 2.0 (the "License");4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7#     http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14"""15Rules to verbalize numbers into Chinese characters.16https://zh.wikipedia.org/wiki/中文数字#現代中文17"""18import re19from collections import OrderedDict20from typing import List21 22DIGITS = {str(i): tran for i, tran in enumerate("零一二三四五六七八九")}23UNITS = OrderedDict(24    {25        1: "十",26        2: "百",27        3: "千",28        4: "万",29        8: "亿",30    }31)32 33COM_QUANTIFIERS = "(封|艘|把|目|套|段|人|所|朵|匹|张|座|回|场|尾|条|个|首|阙|阵|网|炮|顶|丘|棵|只|支|袭|辆|挑|担|颗|壳|窠|曲|墙|群|腔|砣|座|客|贯|扎|捆|刀|令|打|手|罗|坡|山|岭|江|溪|钟|队|单|双|对|出|口|头|脚|板|跳|枝|件|贴|针|线|管|名|位|身|堂|课|本|页|家|户|层|丝|毫|厘|分|钱|两|斤|担|铢|石|钧|锱|忽|(千|毫|微)克|毫|厘|(公)分|分|寸|尺|丈|里|寻|常|铺|程|(千|分|厘|毫|微)米|米|撮|勺|合|升|斗|石|盘|碗|碟|叠|桶|笼|盆|盒|杯|钟|斛|锅|簋|篮|盘|桶|罐|瓶|壶|卮|盏|箩|箱|煲|啖|袋|钵|年|月|日|季|刻|时|周|天|秒|分|小时|旬|纪|岁|世|更|夜|春|夏|秋|冬|代|伏|辈|丸|泡|粒|颗|幢|堆|条|根|支|道|面|片|张|颗|块|元|(亿|千万|百万|万|千|百)|(亿|千万|百万|万|千|百|美|)元|(亿|千万|百万|万|千|百|十|)吨|(亿|千万|百万|万|千|百|)块|角|毛|分)"34 35# 分数表达式36RE_FRAC = re.compile(r"(-?)(\d+)/(\d+)")37 38 39def replace_frac(match) -> str:40    """41    Args:42        match (re.Match)43    Returns:44        str45    """46    sign = match.group(1)47    nominator = match.group(2)48    denominator = match.group(3)49    try:50        import cn2an51 52        return cn2an.an2cn(f"{sign}{nominator}/{denominator}", "low")53    except:54        sign: str = "负" if sign else ""55        nominator: str = num2str(nominator)56        denominator: str = num2str(denominator)57        result = f"{sign}{denominator}分之{nominator}"58    return result59 60 61# 百分数表达式62RE_PERCENTAGE = re.compile(r"(-?)(\d+(\.\d+)?)(%|%)")63 64 65def replace_percentage(match) -> str:66    """67    Args:68        match (re.Match)69    Returns:70        str71    """72    sign = match.group(1)73    percent = match.group(2)74    try:75        import cn2an76 77        return cn2an.an2cn(f"{sign}{percent}%", "low")78    except:79        sign: str = "负" if sign else ""80        percent: str = num2str(percent)81        result = f"{sign}百分之{percent}"82    return result83 84 85# 整数表达式86# 带负号的整数 -1087RE_INTEGER = re.compile(r"(-)" r"(\d+)")88 89 90def replace_negative_num(match) -> str:91    """92    Args:93        match (re.Match)94    Returns:95        str96    """97    sign = match.group(1)98    number = match.group(2)99    try:100        import cn2an101 102        return cn2an.an2cn(f"{sign}{number}", "low")103    except:104        sign: str = "负" if sign else ""105        number: str = num2str(number)106        result = f"{sign}{number}"107    return result108 109 110# 编号-无符号整形111# 00078112RE_DEFAULT_NUM = re.compile(r"\d{3}\d*")113 114 115def replace_default_num(match):116    """117    Args:118        match (re.Match)119    Returns:120        str121    """122    number = match.group(0)123    try:124        import cn2an125 126        return cn2an.an2cn(number, "low")127    except:128        return verbalize_digit(number, alt_one=True)129 130 131# 数字表达式132# 纯小数133RE_DECIMAL_NUM = re.compile(r"(-?)((\d+)(\.\d+))" r"|(\.(\d+))")134# 正整数 + 量词135RE_POSITIVE_QUANTIFIERS = re.compile(r"(\d+)([多余几\+])?" + COM_QUANTIFIERS)136RE_NUMBER = re.compile(r"(-?)((\d+)(\.\d+)?)" r"|(\.(\d+))")137 138 139def replace_positive_quantifier(match) -> str:140    """141    Args:142        match (re.Match)143    Returns:144        str145    """146    number = match.group(1)147    match_2 = match.group(2)148    if match_2 == "+":149        match_2 = "多"150    match_2: str = match_2 if match_2 else ""151    quantifiers: str = match.group(3)152    number: str = num2str(number)153    result = f"{number}{match_2}{quantifiers}"154    return result155 156 157def replace_number(match) -> str:158    """159    Args:160        match (re.Match)161    Returns:162        str163    """164    sign = match.group(1)165    number = match.group(2)166    pure_decimal = match.group(5)167 168    # TODO 也许可以把 num2str 完全替换成 cn2an169    import cn2an170 171    text = pure_decimal if pure_decimal else f"{sign}{number}"172    try:173        result = cn2an.an2cn(text, "low")174    except ValueError:175        if pure_decimal:176            result = num2str(pure_decimal)177        else:178            sign: str = "负" if sign else ""179            number: str = num2str(number)180            result = f"{sign}{number}"181    return result182 183 184# 范围表达式185# match.group(1) and match.group(8) are copy from RE_NUMBER186 187RE_RANGE = re.compile(188    r"((-?)((\d+)(\.\d+)?)|(\.(\d+)))[-~]((-?)((\d+)(\.\d+)?)|(\.(\d+)))"189)190 191 192def replace_range(match) -> str:193    """194    Args:195        match (re.Match)196    Returns:197        str198    """199    first, second = match.group(1), match.group(8)200    first = RE_NUMBER.sub(replace_number, first)201    second = RE_NUMBER.sub(replace_number, second)202    result = f"{first}到{second}"203    return result204 205 206def _get_value(value_string: str, use_zero: bool = True) -> List[str]:207    stripped = value_string.lstrip("0")208    if len(stripped) == 0:209        return []210    elif len(stripped) == 1:211        if use_zero and len(stripped) < len(value_string):212            return [DIGITS["0"], DIGITS[stripped]]213        else:214            return [DIGITS[stripped]]215    else:216        largest_unit = next(217            power for power in reversed(UNITS.keys()) if power < len(stripped)218        )219        first_part = value_string[:-largest_unit]220        second_part = value_string[-largest_unit:]221        return _get_value(first_part) + [UNITS[largest_unit]] + _get_value(second_part)222 223 224def verbalize_cardinal(value_string: str) -> str:225    if not value_string:226        return ""227 228    # 000 -> '零' , 0 -> '零'229    value_string = value_string.lstrip("0")230    if len(value_string) == 0:231        return DIGITS["0"]232 233    result_symbols = _get_value(value_string)234    # verbalized number starting with '一十*' is abbreviated as `十*`235    if (236        len(result_symbols) >= 2237        and result_symbols[0] == DIGITS["1"]238        and result_symbols[1] == UNITS[1]239    ):240        result_symbols = result_symbols[1:]241    return "".join(result_symbols)242 243 244def verbalize_digit(value_string: str, alt_one=False) -> str:245    result_symbols = [DIGITS[digit] for digit in value_string]246    result = "".join(result_symbols)247    if alt_one:248        result = result.replace("一", "幺")249    return result250 251 252def num2str(value_string: str) -> str:253    integer_decimal = value_string.split(".")254    if len(integer_decimal) == 1:255        integer = integer_decimal[0]256        decimal = ""257    elif len(integer_decimal) == 2:258        integer, decimal = integer_decimal259    else:260        raise ValueError(261            f"The value string: '${value_string}' has more than one point in it."262        )263 264    result = verbalize_cardinal(integer)265 266    decimal = decimal.rstrip("0")267    if decimal:268        # '.22' is verbalized as '零点二二'269        # '3.20' is verbalized as '三点二270        result = result if result else "零"271        result += "点" + verbalize_digit(decimal)272    return result273