feat: 生成语音v2版本,兼容多音字问题
This commit is contained in:
@@ -48,7 +48,7 @@
|
|||||||
],
|
],
|
||||||
"于": ["yú", null, "于,于是的于"],
|
"于": ["yú", null, "于,于是的于"],
|
||||||
"亏": ["kuī", null, "亏,亏损的亏"],
|
"亏": ["kuī", null, "亏,亏损的亏"],
|
||||||
"工": ["ɡōnɡ", null, "工,工人的人"],
|
"工": ["ɡōnɡ", null, "工,工人的工"],
|
||||||
"土": ["tǔ", null, "土,土地的土"],
|
"土": ["tǔ", null, "土,土地的土"],
|
||||||
"士": ["shì", null, "士,士气的士"],
|
"士": ["shì", null, "士,士气的士"],
|
||||||
"才": ["cái", null, "才,才能的才"],
|
"才": ["cái", null, "才,才能的才"],
|
||||||
@@ -957,7 +957,6 @@
|
|||||||
"论": [
|
"论": [
|
||||||
["lùn", "讨论", "论,讨论的论"],
|
["lùn", "讨论", "论,讨论的论"],
|
||||||
["lún", "经典名", "论,论语的论"]
|
["lún", "经典名", "论,论语的论"]
|
||||||
// TODO
|
|
||||||
],
|
],
|
||||||
"讼": ["sònɡ", null, "讼,诉讼的讼"],
|
"讼": ["sònɡ", null, "讼,诉讼的讼"],
|
||||||
"农": ["nónɡ", null, "农,农业的农"],
|
"农": ["nónɡ", null, "农,农业的农"],
|
||||||
@@ -1283,8 +1282,8 @@
|
|||||||
"吹": ["chuī", null, "吹,吹气的吹"],
|
"吹": ["chuī", null, "吹,吹气的吹"],
|
||||||
"呜": ["wū", null, "呜,呜咽的呜"],
|
"呜": ["wū", null, "呜,呜咽的呜"],
|
||||||
"吭": [
|
"吭": [
|
||||||
["háng", "喉咙", "吭,吭声,喉咙的声音"],
|
["kēng", "出声", "吭,吭声的吭"],
|
||||||
["kēng", "出声", "吭,吭声的吭"]
|
["háng", "喉咙", "吭,吭声,喉咙的声音"]
|
||||||
],
|
],
|
||||||
"吧": [
|
"吧": [
|
||||||
["bā", "酒吧", "吧,吧吧,语气词"],
|
["bā", "酒吧", "吧,吧吧,语气词"],
|
||||||
@@ -1293,8 +1292,8 @@
|
|||||||
"邑": ["yì", null, "邑,城邑,城市"],
|
"邑": ["yì", null, "邑,城邑,城市"],
|
||||||
"吼": ["hǒu", null, "吼,吼叫,大声叫喊"],
|
"吼": ["hǒu", null, "吼,吼叫,大声叫喊"],
|
||||||
"囤": [
|
"囤": [
|
||||||
["dùn", "储存", "囤,囤积,储存"],
|
["tún", "存粮的器物", "囤,粮囤的囤"],
|
||||||
["tún", "存粮的器物", "囤,粮囤的囤"]
|
["dùn", "储存", "囤,囤积,储存"]
|
||||||
],
|
],
|
||||||
"别": [
|
"别": [
|
||||||
["bié", "分别", "别,别扭,不顺畅"],
|
["bié", "分别", "别,别扭,不顺畅"],
|
||||||
@@ -1343,8 +1342,8 @@
|
|||||||
["tián", "耕作", "佃,佃作的佃"]
|
["tián", "耕作", "佃,佃作的佃"]
|
||||||
],
|
],
|
||||||
"作": [
|
"作": [
|
||||||
["zuō", "手工作坊", "作,作业,作品的作"],
|
["zuò", "创作", "作,工作的作"],
|
||||||
["zuò", "创作", "作,工作的作"]
|
["zuō", "手工作坊", "作,作业,作品的作"]
|
||||||
],
|
],
|
||||||
"伯": [
|
"伯": [
|
||||||
["bó", "伯母", "伯,伯父,伯母"],
|
["bó", "伯母", "伯,伯父,伯母"],
|
||||||
@@ -1403,7 +1402,7 @@
|
|||||||
["qiū", "古国名", "龟,龟兹的龟"]
|
["qiū", "古国名", "龟,龟兹的龟"]
|
||||||
],
|
],
|
||||||
"甸": [
|
"甸": [
|
||||||
["diàn", "郊外", "甸,甸甸的甸"],
|
["diàn", "郊外", "甸,沉甸甸的甸"],
|
||||||
["tián", "古同'田'", "甸,甸田的甸"],
|
["tián", "古同'田'", "甸,甸田的甸"],
|
||||||
["shèng", "古读", "甸,甸师的甸"]
|
["shèng", "古读", "甸,甸师的甸"]
|
||||||
],
|
],
|
||||||
@@ -1692,8 +1691,8 @@
|
|||||||
"抬": ["tái", null, "抬,抬举,抬起"],
|
"抬": ["tái", null, "抬,抬举,抬起"],
|
||||||
"拇": ["mǔ", null, "拇,拇指,大拇指"],
|
"拇": ["mǔ", null, "拇,拇指,大拇指"],
|
||||||
"拗": [
|
"拗": [
|
||||||
["ǎo", "折断", "拗,拗口,弯曲"],
|
|
||||||
["ào", "别扭", "拗,拗口的拗"],
|
["ào", "别扭", "拗,拗口的拗"],
|
||||||
|
["ǎo", "折断", "拗,拗口,弯曲"],
|
||||||
["niù", "固执", "拗,执拗的拗"]
|
["niù", "固执", "拗,执拗的拗"]
|
||||||
],
|
],
|
||||||
"其": [
|
"其": [
|
||||||
@@ -1714,7 +1713,7 @@
|
|||||||
],
|
],
|
||||||
"茂": ["mào", null, "茂,茂盛,繁茂"],
|
"茂": ["mào", null, "茂,茂盛,繁茂"],
|
||||||
"苹": [
|
"苹": [
|
||||||
["píng", "水果", "苹,浮萍,水草"],
|
["píng", "水果", "苹,苹果的苹"],
|
||||||
["pēng", "草名", "苹,苹萦的苹"]
|
["pēng", "草名", "苹,苹萦的苹"]
|
||||||
],
|
],
|
||||||
"苗": ["miáo", null, "苗,幼苗,禾苗"],
|
"苗": ["miáo", null, "苗,幼苗,禾苗"],
|
||||||
@@ -1726,8 +1725,8 @@
|
|||||||
"直": ["zhí", null, "直,正直的直"],
|
"直": ["zhí", null, "直,正直的直"],
|
||||||
"茁": ["zhuó", null, "茁,茁壮的茁"],
|
"茁": ["zhuó", null, "茁,茁壮的茁"],
|
||||||
"茄": [
|
"茄": [
|
||||||
["jiā", "烟草", "茄,茄子的茄"],
|
["qié", "蔬菜", "茄,茄子的茄"],
|
||||||
["qié", "蔬菜", "茄,茄子的茄"]
|
["jiā", "烟草", "茄,雪茄的茄"]
|
||||||
],
|
],
|
||||||
"茎": ["jīnɡ", null, "茎,茎叶的茎"],
|
"茎": ["jīnɡ", null, "茎,茎叶的茎"],
|
||||||
"苔": [
|
"苔": [
|
||||||
@@ -1759,8 +1758,8 @@
|
|||||||
"述": ["shù", null, "述,叙述的述"],
|
"述": ["shù", null, "述,叙述的述"],
|
||||||
"枕": ["zhěn", null, "枕,枕头的枕"],
|
"枕": ["zhěn", null, "枕,枕头的枕"],
|
||||||
"丧": [
|
"丧": [
|
||||||
["sāng", "丧葬", "丧,丧事的丧"],
|
["sàng", "失去", "丧,丧失的丧"],
|
||||||
["sàng", "失去", "丧,丧失的丧"]
|
["sāng", "丧葬", "丧,丧事的丧"]
|
||||||
],
|
],
|
||||||
"或": ["huò", null, "或,或许的或"],
|
"或": ["huò", null, "或,或许的或"],
|
||||||
"画": ["huà", null, "画,画画的画"],
|
"画": ["huà", null, "画,画画的画"],
|
||||||
@@ -1869,7 +1868,7 @@
|
|||||||
"岸": ["àn", null, "岸,河岸的岸"],
|
"岸": ["àn", null, "岸,河岸的岸"],
|
||||||
"岩": ["yán", null, "岩,岩石,地壳的岩石"],
|
"岩": ["yán", null, "岩,岩石,地壳的岩石"],
|
||||||
"帖": [
|
"帖": [
|
||||||
["tiè", "法帖", "帖,帖子,网络上的帖子"],
|
["tiè", "法帖", "帖,字帖的帖"],
|
||||||
["tiě", "帖子", "帖,请帖的帖"],
|
["tiě", "帖子", "帖,请帖的帖"],
|
||||||
["tiē", "顺从", "帖,服帖的帖"]
|
["tiē", "顺从", "帖,服帖的帖"]
|
||||||
],
|
],
|
||||||
@@ -1946,9 +1945,9 @@
|
|||||||
"依": ["yī", null, "依,依靠的依"],
|
"依": ["yī", null, "依,依靠的依"],
|
||||||
"卑": ["bēi", null, "卑,卑微的卑"],
|
"卑": ["bēi", null, "卑,卑微的卑"],
|
||||||
"的": [
|
"的": [
|
||||||
|
["de", "助词", "的,我的的的"],
|
||||||
["dí", "确实", "的,的确的的"],
|
["dí", "确实", "的,的确的的"],
|
||||||
["dì", "靶心", "的,目的的的"],
|
["dì", "靶心", "的,目的的的"]
|
||||||
["de", "助词", "的,我的的的"]
|
|
||||||
],
|
],
|
||||||
"迫": [
|
"迫": [
|
||||||
["pò", "逼迫", "迫,压迫的迫"],
|
["pò", "逼迫", "迫,压迫的迫"],
|
||||||
@@ -1968,8 +1967,8 @@
|
|||||||
],
|
],
|
||||||
"金": ["jīn", null, "金,金子的金"],
|
"金": ["jīn", null, "金,金子的金"],
|
||||||
"刹": [
|
"刹": [
|
||||||
["chà", "瞬间", "刹,刹车的刹"],
|
["shā", "制动", "刹,刹车的刹"],
|
||||||
["shā", "制动", "刹,刹车的刹"]
|
["chà", "瞬间", "刹,刹车的刹"]
|
||||||
],
|
],
|
||||||
"命": ["mìnɡ", null, "命,命运的命"],
|
"命": ["mìnɡ", null, "命,命运的命"],
|
||||||
"肴": ["yáo", null, "肴,菜肴的肴"],
|
"肴": ["yáo", null, "肴,菜肴的肴"],
|
||||||
@@ -2051,8 +2050,8 @@
|
|||||||
"刻": ["kè", null, "刻,雕刻的刻"],
|
"刻": ["kè", null, "刻,雕刻的刻"],
|
||||||
"育": ["yù", null, "育,教育的育"],
|
"育": ["yù", null, "育,教育的育"],
|
||||||
"氓": [
|
"氓": [
|
||||||
["méng", "无赖", "氓,流氓的氓"],
|
["máng", "无赖", "氓,流氓的氓"],
|
||||||
["máng", "古代百姓", "氓,氓隶的氓"]
|
["méng", "古代百姓", "氓,氓隶的氓"]
|
||||||
],
|
],
|
||||||
"闸": ["zhá", null, "闸,闸门的闸"],
|
"闸": ["zhá", null, "闸,闸门的闸"],
|
||||||
"闹": ["nào", null, "闹,热闹的闹"],
|
"闹": ["nào", null, "闹,热闹的闹"],
|
||||||
@@ -2622,10 +2621,7 @@
|
|||||||
"贷": ["dài", null, "贷,贷款,借入或借出的款项"],
|
"贷": ["dài", null, "贷,贷款,借入或借出的款项"],
|
||||||
"顺": ["shùn", null, "顺,顺利,顺畅"],
|
"顺": ["shùn", null, "顺,顺利,顺畅"],
|
||||||
"修": ["xiū", null, "修,修理,修补"],
|
"修": ["xiū", null, "修,修理,修补"],
|
||||||
"俏": [
|
"俏": [["qiào", "好看", "俏,俏皮,活泼"]],
|
||||||
["qiào", "好看", "俏,俏皮,活泼"],
|
|
||||||
["xiào", "古同'肖'", "俏,俏皮的俏"]
|
|
||||||
],
|
|
||||||
"保": ["bǎo", null, "保,保证,担保"],
|
"保": ["bǎo", null, "保,保证,担保"],
|
||||||
"促": ["cù", null, "促,促进,推动"],
|
"促": ["cù", null, "促,促进,推动"],
|
||||||
"俄": ["é", null, "俄,俄罗斯,国家名"],
|
"俄": ["é", null, "俄,俄罗斯,国家名"],
|
||||||
@@ -2634,10 +2630,7 @@
|
|||||||
"俭": ["jiǎn", null, "俭,节俭的俭"],
|
"俭": ["jiǎn", null, "俭,节俭的俭"],
|
||||||
"俗": ["sú", null, "俗,风俗的俗"],
|
"俗": ["sú", null, "俗,风俗的俗"],
|
||||||
"俘": ["fú", null, "俘,俘虏的俘"],
|
"俘": ["fú", null, "俘,俘虏的俘"],
|
||||||
"信": [
|
"信": [["xìn", "书信", "信,信任的信"]],
|
||||||
["xìn", "书信", "信,信任的信"],
|
|
||||||
["shēn", "古同'伸'", "信,信阳的信"]
|
|
||||||
],
|
|
||||||
"皇": ["huánɡ", null, "皇,皇帝的皇"],
|
"皇": ["huánɡ", null, "皇,皇帝的皇"],
|
||||||
"泉": ["quán", null, "泉,泉水的泉"],
|
"泉": ["quán", null, "泉,泉水的泉"],
|
||||||
"鬼": ["ɡuǐ", null, "鬼,鬼怪的鬼"],
|
"鬼": ["ɡuǐ", null, "鬼,鬼怪的鬼"],
|
||||||
@@ -2647,10 +2640,7 @@
|
|||||||
["hóu", "封侯", "侯,诸侯的侯"],
|
["hóu", "封侯", "侯,诸侯的侯"],
|
||||||
["hòu", "地名", "侯,闽侯的侯"]
|
["hòu", "地名", "侯,闽侯的侯"]
|
||||||
],
|
],
|
||||||
"追": [
|
"追": [["zhuī", "追求", "追,追赶的追"]],
|
||||||
["zhuī", "追求", "追,追赶的追"],
|
|
||||||
["duī", "古同'雕'", "追,追琢的追"]
|
|
||||||
],
|
|
||||||
"俊": ["jùn", null, "俊,俊杰的俊"],
|
"俊": ["jùn", null, "俊,俊杰的俊"],
|
||||||
"盾": ["dùn", null, "盾,盾牌的盾"],
|
"盾": ["dùn", null, "盾,盾牌的盾"],
|
||||||
"待": [
|
"待": [
|
||||||
@@ -2682,8 +2672,8 @@
|
|||||||
["pàn", "大号的", "胖,胖袄的胖"]
|
["pàn", "大号的", "胖,胖袄的胖"]
|
||||||
],
|
],
|
||||||
"脉": [
|
"脉": [
|
||||||
["mò", "含情脉脉", "脉,脉搏的脉"],
|
["mài", "血管", "脉,脉搏的脉"],
|
||||||
["mài", "血管", "脉,脉搏的脉"]
|
["mò", "含情脉脉", "脉,含情脉脉的脉"]
|
||||||
],
|
],
|
||||||
"胎": ["tāi", null, "胎,胎儿的胎"],
|
"胎": ["tāi", null, "胎,胎儿的胎"],
|
||||||
"勉": ["miǎn", null, "勉,勉强的勉"],
|
"勉": ["miǎn", null, "勉,勉强的勉"],
|
||||||
@@ -2737,8 +2727,8 @@
|
|||||||
"阀": ["fá", null, "阀,阀门,阀门的阀"],
|
"阀": ["fá", null, "阀,阀门,阀门的阀"],
|
||||||
"阁": ["ɡé", null, "阁,阁楼,阁楼的阁"],
|
"阁": ["ɡé", null, "阁,阁楼,阁楼的阁"],
|
||||||
"差": [
|
"差": [
|
||||||
["chà", "差别", "差,出差,出差的差"],
|
["chà", "差别", "差,差劲的差"],
|
||||||
["chā", "差距", "差,差异的差"],
|
["chā", "差距", "差,差距的差"],
|
||||||
["chāi", "差事", "差,出差的差"],
|
["chāi", "差事", "差,出差的差"],
|
||||||
["cī", "不齐", "差,参差的差"]
|
["cī", "不齐", "差,参差的差"]
|
||||||
],
|
],
|
||||||
@@ -2876,8 +2866,8 @@
|
|||||||
"院": ["yuàn", null, "院,庭院的院"],
|
"院": ["yuàn", null, "院,庭院的院"],
|
||||||
"娃": ["wá", null, "娃,娃娃的娃"],
|
"娃": ["wá", null, "娃,娃娃的娃"],
|
||||||
"姥": [
|
"姥": [
|
||||||
["mǔ", "外婆", "姥,姥姥的姥"],
|
["lǎo", "外祖母", "姥,姥姥的姥"],
|
||||||
["lǎo", "外祖母", "姥,姥姥的姥"]
|
["mǔ", "外婆", "姥,天姥山的姥"]
|
||||||
],
|
],
|
||||||
"姨": ["yí", null, "姨,姨妈的姨"],
|
"姨": ["yí", null, "姨,姨妈的姨"],
|
||||||
"姻": ["yīn", null, "姻,姻亲的姻"],
|
"姻": ["yīn", null, "姻,姻亲的姻"],
|
||||||
@@ -3087,20 +3077,20 @@
|
|||||||
"晓": ["xiǎo", null, "晓,清晨的晓"],
|
"晓": ["xiǎo", null, "晓,清晨的晓"],
|
||||||
"哮": ["xiāo", null, "哮,咆哮的哮"],
|
"哮": ["xiāo", null, "哮,咆哮的哮"],
|
||||||
"唠": [
|
"唠": [
|
||||||
["lào", "聊天", "唠,唠叨的唠"],
|
["lào", "聊天", "唠,唠嗑的唠"],
|
||||||
["láo", "说个不停", "唠,唠叨的唠"]
|
["láo", "说个不停", "唠,唠叨的唠"]
|
||||||
],
|
],
|
||||||
"鸭": ["yā", null, "鸭,鸭子的鸭"],
|
"鸭": ["yā", null, "鸭,鸭子的鸭"],
|
||||||
"晃": [
|
"晃": [
|
||||||
["huǎng", "晃动(快)", "晃,摇晃的晃"],
|
["huàng", "摇动", "晃,摇晃的晃"],
|
||||||
["huàng", "摇动", "晃,摇晃的晃"]
|
["huǎng", "晃动(快)", "晃,晃眼的晃"]
|
||||||
],
|
],
|
||||||
"哺": ["bǔ", null, "哺,喂养的哺"],
|
"哺": ["bǔ", null, "哺,喂养的哺"],
|
||||||
"晌": ["shǎnɡ", null, "晌,午后的晌"],
|
"晌": ["shǎnɡ", null, "晌,午后的晌"],
|
||||||
"剔": ["tī", null, "剔,剔除的剔"],
|
"剔": ["tī", null, "剔,剔除的剔"],
|
||||||
"晕": [
|
"晕": [
|
||||||
["yùn", "头晕", "晕,头晕的晕"],
|
["yūn", "昏厥", "晕,晕倒的晕"],
|
||||||
["yūn", "昏厥", "晕,晕倒的晕"]
|
["yùn", "头晕", "晕,光晕的晕"]
|
||||||
],
|
],
|
||||||
"蚌": [
|
"蚌": [
|
||||||
["bàng", "河蚌", "蚌,蚌壳的蚌"],
|
["bàng", "河蚌", "蚌,蚌壳的蚌"],
|
||||||
@@ -3369,7 +3359,7 @@
|
|||||||
"涉": ["shè", null, "涉,涉及,牵涉到某件事情或某个领域"],
|
"涉": ["shè", null, "涉,涉及,牵涉到某件事情或某个领域"],
|
||||||
"消": ["xiāo", null, "消,消除,去除、去掉某物"],
|
"消": ["xiāo", null, "消,消除,去除、去掉某物"],
|
||||||
"涡": [
|
"涡": [
|
||||||
["wō", "旋涡", "涡,漩涡,水流形成的旋转漏斗状物体"],
|
["wō", "旋涡", "涡,漩涡的涡"],
|
||||||
["guō", "河流名", "涡,涡河的涡"]
|
["guō", "河流名", "涡,涡河的涡"]
|
||||||
],
|
],
|
||||||
"浩": ["hào", null, "浩,浩瀚,形容水势、星空等非常广阔"],
|
"浩": ["hào", null, "浩,浩瀚,形容水势、星空等非常广阔"],
|
||||||
@@ -3454,8 +3444,8 @@
|
|||||||
"谈": ["tán", null, "谈,谈笑风生的谈"],
|
"谈": ["tán", null, "谈,谈笑风生的谈"],
|
||||||
"谊": ["yì", null, "谊,友谊的谊"],
|
"谊": ["yì", null, "谊,友谊的谊"],
|
||||||
"剥": [
|
"剥": [
|
||||||
["bāo", "口语读音", "剥,剥削的剥"],
|
["bō", "剥离", "剥,剥削的剥"],
|
||||||
["bō", "剥离", "剥,剥削的剥"]
|
["bāo", "口语读音", "剥,剥削的剥"]
|
||||||
],
|
],
|
||||||
"恳": ["kěn", null, "恳,恳切的恳"],
|
"恳": ["kěn", null, "恳,恳切的恳"],
|
||||||
"展": ["zhǎn", null, "展,展示的展"],
|
"展": ["zhǎn", null, "展,展示的展"],
|
||||||
@@ -3637,8 +3627,8 @@
|
|||||||
"常": ["chánɡ", null, "常,常常的常"],
|
"常": ["chánɡ", null, "常,常常的常"],
|
||||||
"眶": ["kuànɡ", null, "眶,眼眶的眶"],
|
"眶": ["kuànɡ", null, "眶,眼眶的眶"],
|
||||||
"匙": [
|
"匙": [
|
||||||
["chí", "勺子", "匙,钥匙的匙"],
|
["shi", "开锁工具", "匙,钥匙的匙"],
|
||||||
["shi", "开锁工具", "匙,钥匙的匙"]
|
["chí", "勺子", "匙,汤匙的匙"]
|
||||||
],
|
],
|
||||||
"晨": ["chén", null, "晨,早晨的晨"],
|
"晨": ["chén", null, "晨,早晨的晨"],
|
||||||
"睁": ["zhēnɡ", null, "睁,睁开的睁"],
|
"睁": ["zhēnɡ", null, "睁,睁开的睁"],
|
||||||
@@ -3709,8 +3699,8 @@
|
|||||||
],
|
],
|
||||||
"铐": ["kào", null, "铐,手铐的铐"],
|
"铐": ["kào", null, "铐,手铐的铐"],
|
||||||
"铛": [
|
"铛": [
|
||||||
["chēng", "平底锅", "铛,钟铛的铛"],
|
["dāng", "拟声词", "铛,铛铛的铛"],
|
||||||
["dāng", "拟声词", "铛,铛铛的铛"]
|
["chēng", "平底锅", "铛,钟铛的铛"]
|
||||||
],
|
],
|
||||||
"铝": ["lǚ", null, "铝,铝制的铝"],
|
"铝": ["lǚ", null, "铝,铝制的铝"],
|
||||||
"铜": ["tónɡ", null, "铜,铜钱的钱"],
|
"铜": ["tónɡ", null, "铜,铜钱的钱"],
|
||||||
@@ -4093,8 +4083,8 @@
|
|||||||
],
|
],
|
||||||
"韩": ["hán", null, "韩,韩国的韩"],
|
"韩": ["hán", null, "韩,韩国的韩"],
|
||||||
"朝": [
|
"朝": [
|
||||||
["zhāo", "早晨", "朝,朝鲜的朝"],
|
["cháo", "朝廷", "朝,朝代的朝"],
|
||||||
["cháo", "朝廷", "朝,朝代的朝"]
|
["zhāo", "早晨", "朝,朝阳的朝"]
|
||||||
],
|
],
|
||||||
"辜": ["ɡū", null, "辜,辜月的辜"],
|
"辜": ["ɡū", null, "辜,辜月的辜"],
|
||||||
"葵": ["kuí", null, "葵,向日的葵"],
|
"葵": ["kuí", null, "葵,向日的葵"],
|
||||||
@@ -4135,23 +4125,23 @@
|
|||||||
"粟": ["sù", null, "粟,粟米,粟的粟"],
|
"粟": ["sù", null, "粟,粟米,粟的粟"],
|
||||||
"棘": ["jí", null, "棘,荆棘,棘的棘"],
|
"棘": ["jí", null, "棘,荆棘,棘的棘"],
|
||||||
"酣": ["hān", null, "酣,酣睡,酣的酣"],
|
"酣": ["hān", null, "酣,酣睡,酣的酣"],
|
||||||
"酥": ["sū", null, "酥,指用油类和面粉等制成的软食品,如酥油糕点等"],
|
"酥": ["sū", null, "酥,酥油的酥"],
|
||||||
"厨": ["chú", null, "厨,指负责烹饪食物的厨师"],
|
"厨": ["chú", null, "厨,厨房的厨"],
|
||||||
"厦": [
|
"厦": [
|
||||||
["shà", "高楼", "厦,是厦门市的简称"],
|
["shà", "高楼", "厦,是厦门市的简称"],
|
||||||
["xià", "地名", "厦,厦门的厦"]
|
["xià", "地名", "厦,厦门的厦"]
|
||||||
],
|
],
|
||||||
"硬": ["yìnɡ", null, "硬,形容物体的质地非常坚硬"],
|
"硬": ["yìnɡ", null, "硬,坚硬的硬"],
|
||||||
"硝": ["xiāo", null, "硝,指一种化学物质,常用于制作火药等"],
|
"硝": ["xiāo", null, "硝,硝酸的硝"],
|
||||||
"确": ["què", null, "确,表示肯定、确定的意思"],
|
"确": ["què", null, "确,正确的确"],
|
||||||
"硫": ["liú", null, "硫,是一种化学元素,常用于制作肥料等"],
|
"硫": ["liú", null, "硫,硫磺的硫"],
|
||||||
"雁": ["yàn", null, "雁,一种候鸟,属于鸭科"],
|
"雁": ["yàn", null, "雁,大雁的雁"],
|
||||||
"殖": [
|
"殖": [
|
||||||
["zhí", "殖民", "殖,指生物体通过生殖繁衍后代的过程"],
|
["zhí", "殖民", "殖,殖民的殖"],
|
||||||
["shi", "骨头", "殖,骨殖的殖"]
|
["shi", "骨头", "殖,骨殖的殖"]
|
||||||
],
|
],
|
||||||
"裂": [
|
"裂": [
|
||||||
["liè", "分裂", "裂,指物体表面出现的裂缝或分裂"],
|
["liè", "分裂", "裂,裂缝的裂"],
|
||||||
["liě", "口语", "裂,裂开的裂"]
|
["liě", "口语", "裂,裂开的裂"]
|
||||||
],
|
],
|
||||||
"雄": ["xiónɡ", null, "雄,形容事物雄伟、壮观"],
|
"雄": ["xiónɡ", null, "雄,形容事物雄伟、壮观"],
|
||||||
@@ -4365,8 +4355,8 @@
|
|||||||
["suì", "于是", "遂,遂愿的遂"]
|
["suì", "于是", "遂,遂愿的遂"]
|
||||||
],
|
],
|
||||||
"曾": [
|
"曾": [
|
||||||
["zēng", "隔代", "曾,曾经的曾"],
|
["céng", "曾经", "曾,曾经的曾"],
|
||||||
["céng", "曾经", "曾,曾经的曾"]
|
["zēng", "隔代", "曾,曾孙的曾"]
|
||||||
],
|
],
|
||||||
"焰": ["yàn", null, "焰,火焰的焰"],
|
"焰": ["yàn", null, "焰,火焰的焰"],
|
||||||
"港": [
|
"港": [
|
||||||
@@ -4666,7 +4656,7 @@
|
|||||||
["xiè", "姓氏", "解,解姓的解"]
|
["xiè", "姓氏", "解,解姓的解"]
|
||||||
],
|
],
|
||||||
"煞": [
|
"煞": [
|
||||||
["shā", "极;很", "煞,煞费苦心煞"],
|
["shā", "极;很", "煞,煞费苦心的煞"],
|
||||||
["shà", "凶气", "煞,煞气的煞"]
|
["shà", "凶气", "煞,煞气的煞"]
|
||||||
],
|
],
|
||||||
"雏": ["chú", null, "雏,雏菊雏"],
|
"雏": ["chú", null, "雏,雏菊雏"],
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
{"generated":["一_yi1","乙_yi3","二_er4","十_shi2","丁_ding1","厂_chang3","七_qi1","卜_bu3","八_ba1","人_ren2"],"updatedAt":"2026-02-11T03:30:00.988Z"}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"generate-introduce": "cross-env node scripts/generate_char_introduce.js",
|
"generate-introduce": "cross-env node scripts/generate_char_introduce.js",
|
||||||
"generate-voice": "node scripts/generate_char_voice.js",
|
"generate-voice": "node scripts/generate_char_voice.js",
|
||||||
|
"generate-voice-v2": "node scripts/generate_char_voice_v2.js",
|
||||||
"deploy-assets": "node scripts/deploy-assets.js",
|
"deploy-assets": "node scripts/deploy-assets.js",
|
||||||
"deploy-assets:local": "node scripts/deploy-assets.js --local",
|
"deploy-assets:local": "node scripts/deploy-assets.js --local",
|
||||||
"deploy-assets:server": "node scripts/deploy-assets.js --server"
|
"deploy-assets:server": "node scripts/deploy-assets.js --server"
|
||||||
|
|||||||
@@ -0,0 +1,397 @@
|
|||||||
|
/**
|
||||||
|
* 使用腾讯云语音合成 TTS 为汉字介绍生成语音(v2)
|
||||||
|
* 读取 char_introduce_v2.json,按单音字/多音字取第一条 [pinyin, meaning, introduce],
|
||||||
|
* 用 SSML <phoneme> 仅标注当前字的读音,生成语音保存到 data/voice,命名:汉字_拼音.mp3
|
||||||
|
*/
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const crypto = require('crypto');
|
||||||
|
const https = require('https');
|
||||||
|
require('dotenv').config({ path: path.join(__dirname, '../.env') });
|
||||||
|
|
||||||
|
const SECRET_ID = process.env.TENCENT_SECRET_ID;
|
||||||
|
const SECRET_KEY = process.env.TENCENT_SECRET_KEY;
|
||||||
|
|
||||||
|
const HOST = 'tts.tencentcloudapi.com';
|
||||||
|
const SERVICE = 'tts';
|
||||||
|
const REGION = 'ap-guangzhou';
|
||||||
|
const ACTION = 'TextToVoice';
|
||||||
|
const VERSION = '2019-08-23';
|
||||||
|
|
||||||
|
const VOICE_TYPE = 101015;
|
||||||
|
const CODEC = 'mp3';
|
||||||
|
const SAMPLE_RATE = 16000;
|
||||||
|
|
||||||
|
/** 只处理前 N 个汉字,便于调试;设为 0 或 null 表示不限制 */
|
||||||
|
const MAX_CHAR_COUNT = 10;
|
||||||
|
|
||||||
|
/** 已生成记录文件(存放在 data/temp) */
|
||||||
|
const VOICE_RECORD_FILE_V2 = 'voice_generated_v2.json';
|
||||||
|
|
||||||
|
const SKIP_KEYS = new Set(['_schema', '_verified_before']);
|
||||||
|
|
||||||
|
// 带声调字母 -> 无声调字母
|
||||||
|
const TONE_TO_VOWEL = {
|
||||||
|
ā: 'a',
|
||||||
|
á: 'a',
|
||||||
|
ǎ: 'a',
|
||||||
|
à: 'a',
|
||||||
|
ē: 'e',
|
||||||
|
é: 'e',
|
||||||
|
ě: 'e',
|
||||||
|
è: 'e',
|
||||||
|
ī: 'i',
|
||||||
|
í: 'i',
|
||||||
|
ǐ: 'i',
|
||||||
|
ì: 'i',
|
||||||
|
ō: 'o',
|
||||||
|
ó: 'o',
|
||||||
|
ǒ: 'o',
|
||||||
|
ò: 'o',
|
||||||
|
ū: 'u',
|
||||||
|
ú: 'u',
|
||||||
|
ǔ: 'u',
|
||||||
|
ù: 'u',
|
||||||
|
ǖ: 'v',
|
||||||
|
ǘ: 'v',
|
||||||
|
ǚ: 'v',
|
||||||
|
ǜ: 'v',
|
||||||
|
};
|
||||||
|
// 带声调字母 -> 声调数字 1-4
|
||||||
|
const TONE_TO_NUM = {
|
||||||
|
ā: 1,
|
||||||
|
á: 2,
|
||||||
|
ǎ: 3,
|
||||||
|
à: 4,
|
||||||
|
ē: 1,
|
||||||
|
é: 2,
|
||||||
|
ě: 3,
|
||||||
|
è: 4,
|
||||||
|
ī: 1,
|
||||||
|
í: 2,
|
||||||
|
ǐ: 3,
|
||||||
|
ì: 4,
|
||||||
|
ō: 1,
|
||||||
|
ó: 2,
|
||||||
|
ǒ: 3,
|
||||||
|
ò: 4,
|
||||||
|
ū: 1,
|
||||||
|
ú: 2,
|
||||||
|
ǔ: 3,
|
||||||
|
ù: 4,
|
||||||
|
ǖ: 1,
|
||||||
|
ǘ: 2,
|
||||||
|
ǚ: 3,
|
||||||
|
ǜ: 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将带声调符号的拼音转为腾讯 SSML 用的格式:无声调 + 数字声调(如 chǎng -> chang3)
|
||||||
|
* 无声调拼音(如 le、bo)末尾补 5(轻声)
|
||||||
|
* 数据中可能含 Unicode 印刷体 ɡ (U+0261),需规范为 ASCII g,否则 TTS 报 invalid words
|
||||||
|
*/
|
||||||
|
function pinyinToPhoneme(pinyin) {
|
||||||
|
if (!pinyin || typeof pinyin !== 'string') return 'unknown5';
|
||||||
|
// 规范为 TTS 可识别的拼音字符:ɡ(U+0261)->g, ɑ(U+0251)->a
|
||||||
|
let normalized = pinyin.replace(/\u0261/g, 'g').replace(/\u0251/g, 'a');
|
||||||
|
let tone = 5;
|
||||||
|
let base = '';
|
||||||
|
for (const c of normalized) {
|
||||||
|
if (TONE_TO_VOWEL[c] !== undefined) {
|
||||||
|
base += TONE_TO_VOWEL[c];
|
||||||
|
tone = TONE_TO_NUM[c];
|
||||||
|
} else {
|
||||||
|
base += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ü 在 SSML 中常用 v 表示
|
||||||
|
base = base.replace(/ü/g, 'v');
|
||||||
|
return base + tone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 introduce 中所有「当前字」用 <phoneme> 包裹,只标该字读音
|
||||||
|
*/
|
||||||
|
function buildSSML(introduce, char, pinyinPhoneme) {
|
||||||
|
const tag = `<phoneme alphabet="py" ph="${pinyinPhoneme}">${escapeXml(char)}</phoneme>`;
|
||||||
|
const parts = introduce.split(char);
|
||||||
|
const ssmlBody = parts.join(tag);
|
||||||
|
return `<speak>${ssmlBody}</speak>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeXml(s) {
|
||||||
|
return s
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
}
|
||||||
|
|
||||||
|
function sha256(message, secret = '', encoding = 'hex') {
|
||||||
|
const hmac = secret ? crypto.createHmac('sha256', secret) : crypto.createHash('sha256');
|
||||||
|
return hmac.update(message).digest(encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDate(timestamp) {
|
||||||
|
const date = new Date(timestamp * 1000);
|
||||||
|
const year = date.getUTCFullYear();
|
||||||
|
const month = ('0' + (date.getUTCMonth() + 1)).slice(-2);
|
||||||
|
const day = ('0' + date.getUTCDate()).slice(-2);
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateSignature(secretId, secretKey, host, payload, timestamp) {
|
||||||
|
const date = getDate(timestamp);
|
||||||
|
const httpRequestMethod = 'POST';
|
||||||
|
const canonicalUri = '/';
|
||||||
|
const canonicalQueryString = '';
|
||||||
|
const contentType = 'application/json';
|
||||||
|
const canonicalHeaders = `content-type:${contentType}\nhost:${host}\nx-tc-action:${ACTION.toLowerCase()}\n`;
|
||||||
|
const signedHeaders = 'content-type;host;x-tc-action';
|
||||||
|
const hashedRequestPayload = sha256(payload);
|
||||||
|
const canonicalRequest = `${httpRequestMethod}\n${canonicalUri}\n${canonicalQueryString}\n${canonicalHeaders}\n${signedHeaders}\n${hashedRequestPayload}`;
|
||||||
|
|
||||||
|
const algorithm = 'TC3-HMAC-SHA256';
|
||||||
|
const credentialScope = `${date}/${SERVICE}/tc3_request`;
|
||||||
|
const hashedCanonicalRequest = sha256(canonicalRequest);
|
||||||
|
const stringToSign = `${algorithm}\n${timestamp}\n${credentialScope}\n${hashedCanonicalRequest}`;
|
||||||
|
|
||||||
|
const secretDate = sha256(date, 'TC3' + secretKey, 'buffer');
|
||||||
|
const secretService = sha256(SERVICE, secretDate, 'buffer');
|
||||||
|
const secretSigning = sha256('tc3_request', secretService, 'buffer');
|
||||||
|
const signature = sha256(stringToSign, secretSigning);
|
||||||
|
const authorization = `${algorithm} Credential=${secretId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
||||||
|
|
||||||
|
return authorization;
|
||||||
|
}
|
||||||
|
|
||||||
|
function textToVoice(text) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const timestamp = Math.floor(Date.now() / 1000);
|
||||||
|
const sessionId = `voice-v2-${timestamp}-${Math.random().toString(36).slice(2, 10)}`;
|
||||||
|
|
||||||
|
const payload = JSON.stringify({
|
||||||
|
Text: text,
|
||||||
|
SessionId: sessionId,
|
||||||
|
VoiceType: VOICE_TYPE,
|
||||||
|
PrimaryLanguage: 1,
|
||||||
|
SampleRate: SAMPLE_RATE,
|
||||||
|
Codec: CODEC,
|
||||||
|
Speed: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const authorization = generateSignature(SECRET_ID, SECRET_KEY, HOST, payload, timestamp);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
hostname: HOST,
|
||||||
|
port: 443,
|
||||||
|
path: '/',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Host: HOST,
|
||||||
|
'X-TC-Action': ACTION,
|
||||||
|
'X-TC-Version': VERSION,
|
||||||
|
'X-TC-Timestamp': timestamp.toString(),
|
||||||
|
'X-TC-Region': REGION,
|
||||||
|
Authorization: authorization,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const req = https.request(options, (res) => {
|
||||||
|
let data = '';
|
||||||
|
res.on('data', (chunk) => (data += chunk));
|
||||||
|
res.on('end', () => {
|
||||||
|
try {
|
||||||
|
const result = JSON.parse(data);
|
||||||
|
if (result.Response && result.Response.Audio) {
|
||||||
|
resolve(Buffer.from(result.Response.Audio, 'base64'));
|
||||||
|
} else if (result.Response && result.Response.Error) {
|
||||||
|
reject(new Error(result.Response.Error.Message));
|
||||||
|
} else {
|
||||||
|
reject(new Error('Unknown API response: ' + data));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', reject);
|
||||||
|
req.write(payload);
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件名:汉字_拼音数字.mp3,拼音中的特殊字符(如 ɡ)规范为 ASCII
|
||||||
|
*/
|
||||||
|
function getVoiceFilename(char, pinyinPhoneme) {
|
||||||
|
const safePinyin = pinyinPhoneme.replace(/\s/g, '').replace(/[^\w\-]/g, '_');
|
||||||
|
return `${char}_${safePinyin}.${CODEC}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 生成记录用的 key(与文件名不含扩展名一致) */
|
||||||
|
function getRecordKey(char, pinyinPhoneme) {
|
||||||
|
const safePinyin = pinyinPhoneme.replace(/\s/g, '').replace(/[^\w\-]/g, '_');
|
||||||
|
return `${char}_${safePinyin}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 data/temp 目录加载已生成记录
|
||||||
|
*/
|
||||||
|
function loadVoiceRecordV2(tempDir) {
|
||||||
|
const recordPath = path.join(tempDir, VOICE_RECORD_FILE_V2);
|
||||||
|
let generated = [];
|
||||||
|
if (fs.existsSync(recordPath)) {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(fs.readFileSync(recordPath, 'utf-8'));
|
||||||
|
generated = data.generated || [];
|
||||||
|
} catch (e) {
|
||||||
|
generated = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Set(generated);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将已生成记录写入 data/temp
|
||||||
|
*/
|
||||||
|
function saveVoiceRecordV2(tempDir, generatedList) {
|
||||||
|
const recordPath = path.join(tempDir, VOICE_RECORD_FILE_V2);
|
||||||
|
fs.writeFileSync(
|
||||||
|
recordPath,
|
||||||
|
JSON.stringify({
|
||||||
|
generated: generatedList,
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
}),
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function delay(ms) {
|
||||||
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 char_introduce_v2 的一条 value 解析出 [pinyin, meaning, introduce]
|
||||||
|
* 规则:
|
||||||
|
* - 单音字:一维数组 [pinyin, meaning, introduce],index0=拼音,index2=introduce
|
||||||
|
* - 多音字:二维数组 [[pinyin, meaning, introduce], ...],用第一个元素是 string 还是 array 判断;
|
||||||
|
* 多音字只读第一个子数组,index0=拼音,index2=introduce
|
||||||
|
*/
|
||||||
|
function parseEntry(value) {
|
||||||
|
if (!Array.isArray(value) || value.length === 0) return null;
|
||||||
|
const first = value[0];
|
||||||
|
// 单音字:首元素是 string
|
||||||
|
if (typeof first === 'string') {
|
||||||
|
if (value.length < 3) return null;
|
||||||
|
return [value[0], value[1], value[2]];
|
||||||
|
}
|
||||||
|
// 多音字:首元素是 array,取第一个子数组
|
||||||
|
if (Array.isArray(first) && first.length >= 3) {
|
||||||
|
return [first[0], first[1], first[2]];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 JSON 文件内容中按「出现顺序」提取 key 列表,避免依赖 Object 遍历顺序。
|
||||||
|
* 只匹配行首的 "key":,避免误匹配到值字符串内的引号。
|
||||||
|
*/
|
||||||
|
function getKeyOrderFromJsonText(text) {
|
||||||
|
const keys = [];
|
||||||
|
const re = /^\s*"([^"]+)":\s*(?:\[|")/gm;
|
||||||
|
let m;
|
||||||
|
while ((m = re.exec(text)) !== null) {
|
||||||
|
const key = m[1];
|
||||||
|
if (!SKIP_KEYS.has(key)) {
|
||||||
|
keys.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
if (!SECRET_ID || !SECRET_KEY) {
|
||||||
|
console.error('请在 .env 文件中配置 TENCENT_SECRET_ID 和 TENCENT_SECRET_KEY');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const charDir = path.join(__dirname, '../data/char');
|
||||||
|
const introducePath = path.join(charDir, 'char_introduce_v2.json');
|
||||||
|
const voiceDir = path.join(__dirname, '../data/voice');
|
||||||
|
const tempDir = path.join(__dirname, '../data/temp');
|
||||||
|
|
||||||
|
if (!fs.existsSync(introducePath)) {
|
||||||
|
console.error('未找到 char_introduce_v2.json');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(voiceDir)) {
|
||||||
|
fs.mkdirSync(voiceDir, { recursive: true });
|
||||||
|
}
|
||||||
|
if (!fs.existsSync(tempDir)) {
|
||||||
|
fs.mkdirSync(tempDir, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = fs.readFileSync(introducePath, 'utf-8');
|
||||||
|
const raw = JSON.parse(text);
|
||||||
|
// 按 JSON 文件中 key 的出现顺序构建列表,不依赖 Object 遍历顺序
|
||||||
|
const keyOrder = getKeyOrderFromJsonText(text);
|
||||||
|
const entries = keyOrder
|
||||||
|
.map((char) => {
|
||||||
|
const value = raw[char];
|
||||||
|
const parsed = parseEntry(value);
|
||||||
|
return parsed
|
||||||
|
? { char, pinyin: parsed[0], meaning: parsed[1], introduce: parsed[2] }
|
||||||
|
: null;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
const list = MAX_CHAR_COUNT ? entries.slice(0, MAX_CHAR_COUNT) : entries;
|
||||||
|
const generatedSet = loadVoiceRecordV2(tempDir);
|
||||||
|
const toGenerate = list.filter((item) => {
|
||||||
|
const key = getRecordKey(item.char, pinyinToPhoneme(item.pinyin));
|
||||||
|
return !generatedSet.has(key);
|
||||||
|
});
|
||||||
|
const generatedList = Array.from(generatedSet);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`共 ${entries.length} 个汉字,本次处理前 ${list.length} 个${MAX_CHAR_COUNT ? `(MAX_CHAR_COUNT=${MAX_CHAR_COUNT})` : ''};已生成 ${generatedList.length} 个,待生成 ${toGenerate.length} 个`
|
||||||
|
);
|
||||||
|
|
||||||
|
const DELAY_MS = 200;
|
||||||
|
|
||||||
|
for (let i = 0; i < toGenerate.length; i++) {
|
||||||
|
const { char, pinyin, introduce } = toGenerate[i];
|
||||||
|
const pinyinPhoneme = pinyinToPhoneme(pinyin);
|
||||||
|
const recordKey = getRecordKey(char, pinyinPhoneme);
|
||||||
|
const ssml = buildSSML(introduce, char, pinyinPhoneme);
|
||||||
|
const filename = getVoiceFilename(char, pinyinPhoneme);
|
||||||
|
const filepath = path.join(voiceDir, filename);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const audioBuffer = await textToVoice(ssml);
|
||||||
|
fs.writeFileSync(filepath, audioBuffer);
|
||||||
|
generatedList.push(recordKey);
|
||||||
|
saveVoiceRecordV2(tempDir, generatedList);
|
||||||
|
console.log(
|
||||||
|
`[${i + 1}/${toGenerate.length}] ${char} (${pinyin} -> ${pinyinPhoneme}) -> ${filename}`
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`[${i + 1}/${toGenerate.length}] 失败 ${char}: ${error.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < toGenerate.length - 1) {
|
||||||
|
await delay(DELAY_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`\n语音已保存到: ${voiceDir},已生成记录: ${path.join(tempDir, VOICE_RECORD_FILE_V2)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch(console.error);
|
||||||
Reference in New Issue
Block a user