当前位置: 首页 > news >正文

语音合成(TTS)从零搭建一个完整的TTS系统-第二节-中文转拼音

一、概述

        本节我们进行语音合成前端中的第二步,需要把中文转换为拼音。通过python和c++两种语言进行实现,python可以直接调用pypinyin库实现。c++实现是本节的重点,首先根据词典进行分词,接着把分词后的词进行词典映射,得到最终的拼音串。更多资料和代码可以进入https://t.zsxq.com/qgmoN ,同时欢迎大家提出宝贵的建议,以共同探讨学习。

二、python实现

import logging
import logging.config
import logging.handlers
from pypinyin import pinyin, lazy_pinyin, Style
import re
def to_pinyin(text):pinyin_list = lazy_pinyin(text, style=Style.TONE3, neutral_tone_with_five=True)pinyin_str = " ".join(str(kk) for kk in pinyin_list)pinyin_str = pinyin_str.replace("chong2 neng2", "zhong4 neng2")return pinyin_strdef main():text = '据华尔街日报报道,问题的核心在于百事可乐和可口可乐生产其秘方浓缩糖浆的地点存在差异。'print("origin:{}".format(text))print("convert:{}".format(to_pinyin(text)))text = '这些浓缩糖浆是汽水的精华所在,通常在专门的设施中生产,然后运往装瓶厂,在那里与水、二氧化碳和甜味剂混合制成最终的饮料产品。'print("origin:{}".format(text))print("convert:{}".format(to_pinyin(text)))text = '百事可乐50多年前就开始在爱尔兰生产浓缩糖浆,选址理由是爱尔兰的低企业税率。'print("origin:{}".format(text))print("convert:{}".format(to_pinyin(text)))
if __name__ == '__main__':main()

三、c++工程化

3.1 工程结构

mouse-pinyin
├── CMakeLists.txt
├── bin
│   ├── CMakeLists.txt
│   └── test-pinyin-convert.cc
├── build
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   │   ├── 3.22.1
│   │   │   ├── CMakeCCompiler.cmake
│   │   │   ├── CMakeCXXCompiler.cmake
│   │   │   ├── CMakeDetermineCompilerABI_C.bin
│   │   │   ├── CMakeDetermineCompilerABI_CXX.bin
│   │   │   ├── CMakeSystem.cmake
│   │   │   ├── CompilerIdC
│   │   │   │   ├── CMakeCCompilerId.c
│   │   │   │   ├── a.out
│   │   │   │   └── tmp
│   │   │   └── CompilerIdCXX
│   │   │       ├── CMakeCXXCompilerId.cpp
│   │   │       ├── a.out
│   │   │       └── tmp
│   │   ├── CMakeDirectoryInformation.cmake
│   │   ├── CMakeOutput.log
│   │   ├── CMakeRuleHashes.txt
│   │   ├── CMakeTmp
│   │   ├── Makefile.cmake
│   │   ├── Makefile2
│   │   ├── TargetDirectories.txt
│   │   ├── cmake.check_cache
│   │   ├── libmouse-pinyin.a.dir
│   │   │   ├── DependInfo.cmake
│   │   │   ├── build.make
│   │   │   ├── cmake_clean.cmake
│   │   │   ├── compiler_depend.make
│   │   │   ├── compiler_depend.ts
│   │   │   └── progress.make
│   │   └── progress.marks
│   ├── Makefile
│   ├── bin
│   │   ├── CMakeFiles
│   │   │   ├── CMakeDirectoryInformation.cmake
│   │   │   ├── progress.marks
│   │   │   └── test-pinyin-convert.dir
│   │   │       ├── DependInfo.cmake
│   │   │       ├── build.make
│   │   │       ├── cmake_clean.cmake
│   │   │       ├── compiler_depend.internal
│   │   │       ├── compiler_depend.make
│   │   │       ├── compiler_depend.ts
│   │   │       ├── depend.make
│   │   │       ├── flags.make
│   │   │       ├── link.txt
│   │   │       ├── progress.make
│   │   │       ├── test-pinyin-convert.cc.o
│   │   │       └── test-pinyin-convert.cc.o.d
│   │   ├── Makefile
│   │   ├── cmake_install.cmake
│   │   └── test-pinyin-convert
│   ├── cmake_install.cmake
│   ├── libmouse-pinyin.a
│   └── src
│       ├── CMakeFiles
│       │   ├── CMakeDirectoryInformation.cmake
│       │   ├── mouse-pinyin.dir
│       │   │   ├── DependInfo.cmake
│       │   │   ├── build.make
│       │   │   ├── cmake_clean.cmake
│       │   │   ├── cmake_clean_target.cmake
│       │   │   ├── compiler_depend.internal
│       │   │   ├── compiler_depend.make
│       │   │   ├── compiler_depend.ts
│       │   │   ├── depend.make
│       │   │   ├── flags.make
│       │   │   ├── link.txt
│       │   │   ├── mouse-pinyin-alphabet.cc.o
│       │   │   ├── mouse-pinyin-alphabet.cc.o.d
│       │   │   ├── mouse-pinyin-api.cc.o
│       │   │   ├── mouse-pinyin-api.cc.o.d
│       │   │   ├── mouse-pinyin-handle.cc.o
│       │   │   ├── mouse-pinyin-handle.cc.o.d
│       │   │   ├── mouse-pinyin-log.cc.o
│       │   │   ├── mouse-pinyin-log.cc.o.d
│       │   │   ├── mouse-pinyin-model.cc.o
│       │   │   ├── mouse-pinyin-model.cc.o.d
│       │   │   ├── mouse-pinyin-pinyin.cc.o
│       │   │   ├── mouse-pinyin-pinyin.cc.o.d
│       │   │   ├── mouse-pinyin-split.cc.o
│       │   │   ├── mouse-pinyin-split.cc.o.d
│       │   │   └── progress.make
│       │   └── progress.marks
│       ├── Makefile
│       ├── cmake_install.cmake
│       └── libmouse-pinyin.a
├── src
│   ├── CMakeLists.txt
│   ├── mouse-pinyin-alphabet.cc
│   ├── mouse-pinyin-alphabet.h
│   ├── mouse-pinyin-api.cc
│   ├── mouse-pinyin-api.h
│   ├── mouse-pinyin-handle.cc
│   ├── mouse-pinyin-handle.h
│   ├── mouse-pinyin-log.cc
│   ├── mouse-pinyin-log.h
│   ├── mouse-pinyin-model.cc
│   ├── mouse-pinyin-model.h
│   ├── mouse-pinyin-pinyin.cc
│   ├── mouse-pinyin-pinyin.h
│   ├── mouse-pinyin-split.cc
│   └── mouse-pinyin-split.h
└── test├── lexicon.txt├── print_char.py├── test-pinyin-convert -> ../build/bin/test-pinyin-convert└── test.txt

3.2 核心代码

(1) mouse-pinyin-api.h

/**@author : aflyingwolf*@date   : 2025.4.20*@file   : mouse-pinyin-api.h* */
#ifndef __MOUSE_PINYIN_API_H__
#define __MOUSE_PINYIN_API_H__#ifdef __cplusplus
extern "C"
{
#endiftypedef struct _pinyin_result {char result[2048];int len;
} pinyin_result;void *api_create_mouse_pinyin_res(const char *path);
void api_destroy_mouse_pinyin_res(void *res);void *api_create_mouse_pinyin_handle(void *res);
int api_process_mouse_pinyin(void *handle, const char *text, pinyin_result *res);
void api_destroy_mouse_pinyin_handle(void *handle);#ifdef __cplusplus
}
#endif#endif

(2) mouse-pinyin-api.cc

/**@author : aflyingwolf*@date   : 2025.4.20*@file   : mouse-pinyin-api.cc* */
#include "mouse-pinyin-api.h"
#include "mouse-pinyin-handle.h"
#include <string.h>using namespace mouse_pinyin;void *api_create_mouse_pinyin_res(const char *path) {PinyinModel *model = new PinyinModel();model->Load(path);return (void*)model;
}void api_destroy_mouse_pinyin_res(void *res) {PinyinModel *res_ = (PinyinModel*)res;delete res_;
}void *api_create_mouse_pinyin_handle(void *res) {PinyinModel *res_ = (PinyinModel*)res;PinyinHandle *handle = new PinyinHandle(res_);return (void*)handle;
}void api_destroy_mouse_pinyin_handle(void *handle) {PinyinHandle *handle_ = (PinyinHandle*)handle;delete handle_;
}int api_process_mouse_pinyin(void *handle, const char *text, pinyin_result *res) {PinyinHandle *handle_ = (PinyinHandle*)handle;std::string ret = handle_->Process(text);memset(res->result, 0, 2048);if (ret.size() < 2048) {memcpy(res->result, ret.c_str(), ret.size());res->len = ret.size();} else {memcpy(res->result, ret.c_str(), 2047);res->len = 2047;}return 0;
}

(3) mouse-pinyin-handle.h

/**@author : aflyingwolf*@date   : 2025.4.20*@file   : mouse-pinyin-handle.h* */
#ifndef __MOUSE_PINYIN_HANDLE_H__
#define __MOUSE_PINYIN_HANDLE_H__#include <stdio.h>
#include <map>
#include <string>
#include <vector>
#include "mouse-pinyin-model.h"namespace mouse_pinyin {
class PinyinHandle {public:PinyinHandle(PinyinModel *res);~PinyinHandle();std::string Process(std::string text);private:std::vector<std::string> Split(const std::string str, const std::string pattern);private:PinyinModel *res_;
}; // end class}
#endif

(4) mouse-pinyin-handle.cc

/**@author : aflyingwolf*@date   : 2025.4.20*@file   : mouse-pinyin-handle.cc* */#include "mouse-pinyin-handle.h"
#include "mouse-pinyin-log.h"
#include <string.h>
#include <stdlib.h>namespace mouse_pinyin {PinyinHandle::PinyinHandle(PinyinModel *res) {res_ = res;
}PinyinHandle::~PinyinHandle() {
}std::string PinyinHandle::Process(std::string text) {std::string split_res = res_->split_handle_->Split(text.c_str());LOG_INFO("split_res:%s", split_res.c_str());std::vector<std::string> split_vec = this->Split(split_res, " ");std::string result("");for (int i = 0; i < split_vec.size(); i++) {LOG_INFO("words:%s", split_vec[i].c_str());std::string local_result = res_->pinyin_handle_->GetPinyin(split_vec[i].c_str());if (i != 0 && local_result != "") {result += " ";}result += local_result;}LOG_INFO("pinyin_res:%s", result.c_str());return result;
}std::vector<std::string> PinyinHandle::Split(const std::string str, const std::string pattern) {std::vector<std::string> ret;if (pattern.empty())return ret;int start = 0, index = str.find_first_of(pattern,0);while (index != str.npos) {if (start != index) {ret.push_back(str.substr(start,index-start));}start = index+1;index=str.find_first_of(pattern,start);}if (!str.substr(start).empty()) {ret.push_back(str.substr(start));}return ret;
}}

(5) mouse-pinyin-pinyin.cc

/**@author : aflyingwolf*@date   : 2025.4.20*@file   : mouse-pinyin-pinyin.cc* */#include "mouse-pinyin-pinyin.h"
#include "mouse-pinyin-log.h"
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <string>
namespace mouse_pinyin {PinyinPinyin::PinyinPinyin() {
}PinyinPinyin::~PinyinPinyin() {Destroy();
}int PinyinPinyin::LoadSyms(const char *filename) {#define MAX_LINE_LEN 512LOG_INFO("pinyin load lexicon start.");char line[MAX_LINE_LEN];FILE *fp = fopen(filename, "r");if (fp == NULL) {LOG_ERROR("open file failed.");return -1;}memset(line, 0, MAX_LINE_LEN);while (fgets(line, MAX_LINE_LEN, fp)) {/*char syms[MAX_LINE_LEN];memset(syms, 0, MAX_LINE_LEN);char pinyin[MAX_LINE_LEN];memset(pinyin, 0, MAX_LINE_LEN);if (sscanf(line, "%s\t%s\n", syms, pinyin) != 2) {LOG_ERROR("line:%s error\n", line);return -1;}*/if (line[strlen(line)-1] == '\n') {line[strlen(line)-1] = 0;}std::vector<std::string> element = Split(line, "\t");std::pair<std::string, std::string> elem(element[0], element[1]);str2pinyin_.insert(elem);memset(line, 0, MAX_LINE_LEN);}fclose(fp);LOG_INFO("pinyin load lexicon finish.");return 0;
}std::string PinyinPinyin::GetPinyin(const char *words) {auto is_find = str2pinyin_.find(words);if (is_find == str2pinyin_.end()){LOG_ERROR("not find %s", words);return "";}return is_find->second;
}void PinyinPinyin::Destroy() {
}std::vector<std::string> PinyinPinyin::Split(const std::string str, const std::string pattern) {std::vector<std::string> ret;if (pattern.empty())return ret;int start = 0, index = str.find_first_of(pattern,0);while (index != str.npos) {if (start != index) {ret.push_back(str.substr(start,index-start));}start = index+1;index=str.find_first_of(pattern,start);}if (!str.substr(start).empty()) {ret.push_back(str.substr(start));}return ret;
}
}

(6) mouse-pinyin-pinyin.h

/**@author : aflyingwolf*@date   : 2025.4.20*@file   : mouse-pinyin-pinyin.h* */
#ifndef __MOUSE_PINYIN_PINYIN_H__
#define __MOUSE_PINYIN_PINYIN_H__#include <stdio.h>
#include <map>
#include <string>
#include <vector>
namespace mouse_pinyin {
class PinyinPinyin {public:PinyinPinyin();~PinyinPinyin();int LoadSyms(const char *filename);std::string GetPinyin(const char *words);private:void Destroy();std::vector<std::string> Split(const std::string str, const std::string pattern);private:std::map<std::string, std::string> str2pinyin_;
}; // end class}
#endif

(7) mouse-pinyin-split.cc

/**@author : aflyingwolf*@date   : 2025.4.20*@file   : mouse-pinyin-split.cc* */#include "mouse-pinyin-split.h"
#include "mouse-pinyin-log.h"
#include <string.h>
#include <stdlib.h>namespace mouse_pinyin {PinyinSplit::PinyinSplit(){m_forward_roots = NULL;m_backward_roots = NULL;
}PinyinSplit::~PinyinSplit(){Destroy();
}int PinyinSplit::Init(){if(m_forward_roots == NULL){m_forward_roots = new TrieLexicon();}if(m_backward_roots == NULL){m_backward_roots = new TrieLexicon();}return 0;
}int PinyinSplit::Load(const char *lexicon_filename){LOG_INFO("split load res start.");Init();FILE *fp = fopen(lexicon_filename, "r");if(fp == NULL){LOG_ERROR("fp == null.\n");return -1;}char line1[1024];char line[1024];char tmp_tmp[1024];char array[1024][128];while(fgets(line1, 1024, fp)){int word_num = 0;bzero(line, sizeof(line));sscanf(line1, "%s\t%s\n", line, tmp_tmp);for(int i = 0; i < strlen(line) - 1;){int nbytes = GetNbytes(line + i);char tmpBuffer[128];bzero(tmpBuffer, sizeof(tmpBuffer));for(int j = 0; j < nbytes; j++){tmpBuffer[j] = line[i+j];}i += nbytes;memcpy(array[word_num++], tmpBuffer, sizeof(tmpBuffer));}InsertForwardTree(array, word_num);InsertBackwardTree(array, word_num);}fclose(fp);LOG_INFO("split load res finish.");return 0;
}int PinyinSplit::InsertForwardTree(char array[][128], int num){TrieLexicon *pos = this->m_forward_roots;for(int i = 0; i < num; i++){auto is_find = pos->map.find(array[i]);if(is_find == pos->map.end()){std::pair<std::string, TrieLexicon*> elem(array[i], new TrieLexicon);elem.second->parent = pos;elem.second->value = std::string(array[i]);pos->map.insert(elem);is_find = pos->map.find(array[i]);}pos = is_find->second;}pos->flag = true;return 0;
}int PinyinSplit::InsertBackwardTree(char array[][128], int num){TrieLexicon *pos = this->m_backward_roots;for(int i = num - 1; i >= 0; i--){auto is_find = pos->map.find(array[i]);if(is_find == pos->map.end()){std::pair<std::string, TrieLexicon*> elem(array[i], new TrieLexicon);elem.second->parent = pos;elem.second->value = std::string(array[i]);pos->map.insert(elem);is_find = pos->map.find(array[i]);}pos = is_find->second;}pos->flag = true;return 0;
}std::string PinyinSplit::Split(const char *line){char array[1024][128];int word_num = 0;char tmpBuffer[128];bzero(tmpBuffer, sizeof(tmpBuffer));tmpBuffer[0] = '\n';memcpy(array[word_num++], tmpBuffer, sizeof(tmpBuffer));for(int i = 0; i < strlen(line);){int nbytes = GetNbytes(line + i);bzero(tmpBuffer, sizeof(tmpBuffer));for(int j = 0; j < nbytes; j++){tmpBuffer[j] = line[i+j];}i += nbytes;memcpy(array[word_num++], tmpBuffer, sizeof(tmpBuffer));}bzero(tmpBuffer, sizeof(tmpBuffer));tmpBuffer[0] = '\n';memcpy(array[word_num++], tmpBuffer, sizeof(tmpBuffer));int forward_num = ForwardSplit(array + 1, word_num -1);int backward_num = BackwardSplit(array, word_num - 1);if (forward_num > 10000 && backward_num > 10000) {return "";} else if(forward_num <= backward_num){return m_forward_line;}else{return m_backward_line;}return "";
}int PinyinSplit::ForwardSplit(char array[][128], int num){auto pos = m_forward_roots;int word_num = 0;m_forward_line = "";int loop_size = 0;int max_loop_size = 100000;for(int i = 0; i < num;){loop_size++;if (loop_size > max_loop_size) {return 100000;}auto is_find = pos->map.find(array[i]);if(is_find != pos->map.end()){pos = is_find->second;i++;}else{if(pos == m_forward_roots && array[i][0] != '\n'){LOG_ERROR("%s not exit.", array[i]);i++;continue;}TrieLexicon *tmp = pos;bool tmp_flag = false;std::string str;while(tmp){loop_size++;if (loop_size > max_loop_size) {return 100000;}if(tmp_flag || tmp->flag){// || tmp == m_forward_roots){tmp_flag = true;}else if (tmp == m_forward_roots) {;}else{i--;}if(tmp_flag == true){str = tmp->value + str;}tmp = tmp->parent;}if(tmp_flag == false){LOG_ERROR("%s not exit.", array[i]);i++;} else {m_forward_line += str + " ";word_num++;}pos = m_forward_roots;if(i >= num - 1){break;}}}return word_num;
}int PinyinSplit::BackwardSplit(char array[][128], int num){auto pos = m_backward_roots;int word_num = 0;m_backward_line = "";int loop_size = 0;int max_loop_size = 100000;for(int i = num - 1; i >= 0;){loop_size++;if (loop_size > max_loop_size) {return 100000;}auto is_find = pos->map.find(array[i]);if(is_find != pos->map.end()){pos = is_find->second;i--;}else{if(pos == m_backward_roots && array[i][0] != '\n'){LOG_ERROR("%s not exit.", array[i]);i--;continue;}TrieLexicon *tmp = pos;bool tmp_flag = false;std::string str;while(tmp){loop_size++;if (loop_size > max_loop_size) {return 100000;}if(tmp_flag || tmp->flag){// || tmp == m_backward_roots){tmp_flag = true;}else if(tmp == m_backward_roots){;}else{i++;}if(tmp_flag == true){str = str + tmp->value;}tmp = tmp->parent;}if (tmp_flag == false) {LOG_ERROR("%s not exit.", array[i]);i--;} else {m_backward_line = str + " " + m_backward_line;word_num++;}pos = m_backward_roots;if(i <= 0){break;}}}return word_num;
}void PinyinSplit::Destroy(){Delete(m_forward_roots);Delete(m_backward_roots);
}void PinyinSplit::Delete(TrieLexicon *node) {if (node == NULL) {return;}std::map<std::string, TrieLexicon*>::iterator iter;iter = node->map.begin();while (iter != node->map.end()) {Delete(iter->second);iter++;}delete node;
}int PinyinSplit::GetNbytes(const char *buf){char firstByte = buf[0];int offset = 1;if(firstByte & kFirstBitMask){if(firstByte & kThirdBitMask){if(firstByte & kFourthBitMask){offset = 4;}else{offset = 3;}}else{offset = 2;}}return offset;
}}

(8) mouse-pinyin-split.h

/**@author : aflyingwolf*@date   : 2025.4.20*@file   : mouse-pinyin-split.h* */
#ifndef __MOUSE_PINYIN_SPLIT_H__
#define __MOUSE_PINYIN_SPLIT_H__#include <stdio.h>
#include <map>
#include <string>
namespace mouse_pinyin {typedef struct _TrieLexicon{_TrieLexicon(){flag = false;parent = NULL;}std::map<std::string, struct _TrieLexicon*> map;bool flag;std::string value;struct _TrieLexicon *parent;
} TrieLexicon;const unsigned char kFirstBitMask = 128; // 1000000
const unsigned char kSecondBitMask = 64; // 0100000
const unsigned char kThirdBitMask = 32; // 0010000
const unsigned char kFourthBitMask = 16; // 0001000
const unsigned char kFifthBitMask = 8; // 0000100class PinyinSplit{public:PinyinSplit();~PinyinSplit();int Load(const char *lexicon_filename);std::string Split(const char *line);void Destroy();private:int Init();int GetNbytes(const char *buf);int ForwardSplit(char array[][128], int num);int InsertBackwardTree(char array[][128], int num);int InsertForwardTree(char array[][128], int num);int BackwardSplit(char array[][128], int num);void Delete(TrieLexicon *node);private:TrieLexicon *m_forward_roots;TrieLexicon *m_backward_roots;std::string m_forward_line;std::string m_backward_line;
};}
#endif

(9) CMakeLists.txt

cmake_minimum_required(VERSION 3.22)
project("mouse-pinyin")
set(NAME "mouse-pinyin")
set(LIBNAME "lib${NAME}.a")
set(OUTPUT_LIB "lib${NAME}.a")#SET(CMAKE_BUILD_TYPE "DEBUG")
SET(CMAKE_BUILD_TYPE "RELEASE")SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -fPIC -g -Wno-deprecated")
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -fPIC -O3 -Wall -Wno-deprecated")
SET(CMAKE_C_FLAGS_RELEASE "$ENV{CFLAGS} -fPIC -O3 -Wall -Wno-deprecated")MESSAGE(STATUS "CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}")
MESSAGE(STATUS "CMAKE_C_FLAGS_RELEASE: ${CMAKE_C_FLAGS_RELEASE}")add_compile_options(-std=c++11)
SET(BUILD_PATH ${CMAKE_BINARY_DIR})
SET(SRC_PATH ${CMAKE_SOURCE_DIR})
SET(LIB_PATH ${CMAKE_SOURCE_DIR}/lib)include_directories(${SRC_PATH}/src ${SRC_PATH}/include)link_directories(${BUILD_PATH})
add_subdirectory(src)
add_subdirectory(bin)ADD_CUSTOM_TARGET(${LIBNAME} ALLCOMMAND cp src/${LIBNAME} ${OUTPUT_LIB}COMMAND echo CREATE ${OUTPUT_LIB} > ar.macCOMMAND echo SAVE >> ar.macCOMMAND echo END >> ar.macCOMMAND echo OPEN ${OUTPUT_LIB} > ar.macCOMMAND echo SAVE >> ar.macCOMMAND echo END >> ar.macCOMMAND ar -M < ar.macCOMMAND rm ar.macWORKING_DIRECTORY ${BUILD_PATH})
ADD_DEPENDENCIES(${LIBNAME} ${NAME})

3.3 demo

#include <stdio.h>
#include <string.h>
#include "mouse-pinyin-api.h"
int main(int argc, char *argv[]) {if (argc < 3) {printf("%s input lexicon.txt and test-file\n", argv[0]);return -1;}void *res = api_create_mouse_pinyin_res(argv[1]);void *handle = api_create_mouse_pinyin_handle(res);FILE *fp = fopen(argv[2], "r");char line[1024];memset(line, 0, sizeof(line));while(fgets(line, 1024, fp) != NULL){if (line[0] == '#') {continue;} else {pinyin_result result;int len = strlen(line);if (line[len-1] == '\n') {line[len-1] = 0;}api_process_mouse_pinyin(handle, line, &result);printf("origin:%s\n", line);printf("convet:%s\n\n\n", result.result);}}fclose(fp);api_destroy_mouse_pinyin_handle(handle);api_destroy_mouse_pinyin_res(res);return 0;
}

四、结果演示

4.1 python demo1

origin:据华尔街日报报道,问题的核心在于百事可乐和可口可乐生产其秘方浓缩糖浆的地点存在差异。
convert:ju4 hua2 er3 jie1 ri4 bao4 bao4 dao4 , wen4 ti2 de5 he2 xin1 zai4 yu2 bai3 shi4 ke3 le4 he2 ke3 kou3 ke3 le4 sheng1 chan3 qi2 mi4 fang1 nong2 suo1 tang2 jiang1 de5 di4 dian3 cun2 zai4 cha1 yi4 。
origin:这些浓缩糖浆是汽水的精华所在,通常在专门的设施中生产,然后运往装瓶厂,在那里与水、二氧化碳和甜味剂混合制成最终的饮料产品。
convert:zhe4 xie1 nong2 suo1 tang2 jiang1 shi4 qi4 shui3 de5 jing1 hua2 suo3 zai4 , tong1 chang2 zai4 zhuan1 men2 de5 she4 shi1 zhong1 sheng1 chan3 , ran2 hou4 yun4 wang3 zhuang1 ping2 chang3 , zai4 na4 li3 yu3 shui3 、 er4 yang3 hua4 tan4 he2 tian2 wei4 ji4 hun4 he2 zhi4 cheng2 zui4 zhong1 de5 yin3 liao4 chan3 pin3 。
origin:百事可乐50多年前就开始在爱尔兰生产浓缩糖浆,选址理由是爱尔兰的低企业税率。
convert:bai3 shi4 ke3 le4 50 duo1 nian2 qian2 jiu4 kai1 shi3 zai4 ai4 er3 lan2 sheng1 chan3 nong2 suo1 tang2 jiang1 , xuan3 zhi3 li3 you2 shi4 ai4 er3 lan2 de5 di1 qi3 ye4 shui4 lv4 。

4.2 c++ demo2

origin:今年的花卉长势很好
convet:jin1 nian2 de5 hua1 hui4 zhang3 shi4 hen3 hao3origin:你太唠叨了
convet:ni3 tai4 lao2 dao1 le5origin:如果是棵小草,即使在最好的企业里,你也长不成大树。果真如此,不如历经风雨,把自己培养成名贵花卉。
convet:ru2 guo3 shi4 ke1 xiao3 cao3 ji2 shi3 zai4 zui4 hao3 de5 qi3 ye4 li3 ni3 ye3 zhang3 bu4 cheng2 da4 shu4 guo3 zhen1 ru2 ci3 bu4 ru2 li4 jing1 feng1 yu3 ba3 zi4 ji3 pei2 yang3 cheng2 ming2 gui4 hua1 hui4origin:“总”要为爱人着想,“经”得起爱人唠叨,“理”应对爱人谦让,男人应当“总经理”。
convet: zong3 yao4 wei4 ai4 ren5 zhuo2 xiang3 jing1 de2 qi3 ai4 ren5 lao2 dao1 li3 ying4 dui4 ai4 ren5 qian1 rang4 nan2 ren2 ying1 dang1 zong3 jing1 li3

五、总结

        本节我们对中文进行了转拼音操作,并进行了算法的工程化。通过词典映射解决了大部分多音字的问题,通常情况下,我们还需要做韵律预测,特别是传统方法对韵律预测依赖性更强。我们使用端到端模型,使得韵律信息可以和声学模型一起训练,这样对韵律模型有所弱化,也是端到端模型的主要优势,当然加上韵律模型效果会更好,可控性更强。

相关文章:

语音合成(TTS)从零搭建一个完整的TTS系统-第二节-中文转拼音

一、概述 本节我们进行语音合成前端中的第二步&#xff0c;需要把中文转换为拼音。通过python和c两种语言进行实现&#xff0c;python可以直接调用pypinyin库实现。c实现是本节的重点&#xff0c;首先根据词典进行分词&#xff0c;接着把分词后的词进行词典映射&#xff0c;得到…...

基于springboot的停车位管理系统(源码+数据库)

12基于springboot的停车位管理系统&#xff1a;前端 thymeleaf、Jquery、bootstrap&#xff0c;后端 Springboot、Mybatis&#xff0c;系统角色分为&#xff1a;用户、管理员&#xff0c;管理员在管理后台录入车位信息&#xff0c;用户在线查找车位、预约车位&#xff0c;解决停…...

深入理解 Spring @Configuration 注解

在 Spring 框架中,@Configuration 注解是一个非常重要的工具,它用于定义配置类,这些类可以包含 Bean 定义方法。通过使用 @Configuration 和 @Bean 注解,开发者能够以编程方式创建和管理应用程序上下文中的 Bean。本文将详细介绍 @Configuration 注解的作用、如何使用它以及…...

15.三数之和(LeetCode)java

个人理解&#xff1a; 1.使用双指针做法&#xff0c;首先对数组进行排序 第一重for循环控制第一个数&#xff0c;对数组进行遍历。双指针初始化为lefti1, rigthnums.length-1。然后使用while循环移动双指针寻找合适的数。因为返回的是数&#xff0c;不是下标&#xff0c;数不能…...

2022年全国职业院校技能大赛 高职组 “大数据技术与应用” 赛项赛卷(10卷)任务书

2022年全国职业院校技能大赛 高职组 “大数据技术与应用” 赛项赛卷&#xff08;10卷&#xff09;任务书 模块A&#xff1a;大数据平台搭建&#xff08;容器环境&#xff09;&#xff08;15分&#xff09;任务一&#xff1a;Hadoop 伪分布式安装配置任务二&#xff1a;Flume安装…...

Redis—内存淘汰策略

记&#xff1a;全体LRU&#xff0c;ttl LRU&#xff0c;全体LFU&#xff0c;ttl LFU&#xff0c;全体随机&#xff0c;ttl随机&#xff0c;最快过期&#xff0c;不淘汰&#xff08;八种&#xff09; Redis 实现的是一种近似 LRU 算法&#xff0c;目的是为了更好的节约内存&…...

新能源汽车可视化大屏系统毕业设计

以下是一个基于Python和Flask框架的新能源汽车可视化大屏系统后台代码示例。这个系统提供API接口用于前端大屏展示新能源汽车相关数据。 主应用文件 (app.py) python from flask import Flask, jsonify, request from flask_cors import CORS import random from datetime imp…...

02-keil5的配置和使用

一、创建工程 1、在菜单栏”Project”&#xff0c;在弹出的下拉菜单&#xff0c;选择“New uVision Project”。 2、在弹出的对话框&#xff0c;填写工程的名字&#xff0c;例如工程名字为project。 3、为保存的工程&#xff0c;选择对应的芯片。 4、为当前工程&#xff0c;添…...

电脑硬盘丢失怎么找回?解决硬盘数据恢复的2种方法

无论是个人用户还是企业用户来讲&#xff0c;存储在磁盘中的文档、图片、视频、音频等数据都具有相当的价值。但在日常使用过程中&#xff0c;误删操作、病毒攻击、硬件故障等情况都可能造成电脑硬盘突然消失不见数据丢失。面对电脑硬盘丢失这类问题时&#xff0c;采取正确的应…...

【Spring】依赖注入的方式:构造方法、setter注入、字段注入

在Spring框架中&#xff0c;除了构造器注入&#xff08;Constructor Injection&#xff09;和Setter注入&#xff08;Setter Injection&#xff09;&#xff0c;还有一种依赖注入方式&#xff1a;字段注入&#xff08;Field Injection&#xff09;。字段注入通过在Bean的字段上…...

涨薪技术|0到1学会性能测试第22课-关联函数web_reg_save_param_ex

前面的推文我们掌握了性能测试脚本开发3种常见的关联技术,今天开始给大家分享关联函数web_reg_save_param_ex,后续文章都会系统分享干货! LoadRunner最新版本中,使用的关联函数为web_reg_save_param_ex,以前的版本使用的关联函数为web_reg_save_param,但这两个函数实质差…...

Vue 的数据代理机制

2025/4/22 向 一、什么是数据代理机制 通过访问代理对象的属性&#xff0c;来间接访问目标对象的属性&#xff0c;数据代理机制的实现需要依赖Object.defineProperty()方法。 如下所示&#xff1a; <!DOCTYPE html> <html lang"en"> <head><…...

Android-KeyStore安全的存储系统

​ 在 Android 中&#xff0c;AndroidKeyStore 是一个安全的存储系统&#xff0c;用于存储加密密钥。它提供了一种安全的方式来生成、存储和管理密钥&#xff0c;而无需将密钥暴露给应用程序本身。以下是如何使用 AndroidKeyStore 的基本步骤和示例代码。 检查 AndroidKeyStor…...

部署私有gitlab网站

以下是建立私有 GitLab 代码版本维护平台的完整步骤&#xff0c;涵盖环境准备、安装配置、初始化及日常管理&#xff0c;适用于企业/团队内部代码托管&#xff1a; 一、环境准备 1. 服务器要求&#xff08;最低配置&#xff09; 用途CPU内存存储系统要求小型团队&#xff08…...

(区间 dp)洛谷 P6879 JOI2020 Collecting Stamps 3 题解

题意 给定一个周长为 L L L 的圆&#xff0c;从一个点出发&#xff0c;有 N N N 个黑白熊雕像&#xff0c;编号为 1 1 1 到 N N N&#xff0c;第 i i i 个雕像在顺时针 X i X_i Xi​ 米处&#xff0c;如果你没有在 T i T_i Ti​ 秒内收集到这个黑白熊雕像&#xff0c;那…...

AtCoder 第402场初级竞赛 A~E题解

A CBC 【题目链接】 原题链接:A - CBC 【考点】 枚举 【题目大意】 找出所有的大写字母 【解析】 遍历字符串,判断是否为大写字母,如果是则输出。 【难度】 GESP二级 【代码参考】 #include <bits/stdc++.h> using namespace std;int main() {string s;ci…...

驱动开发硬核特训 · Day 17:深入掌握中断机制与驱动开发中的应用实战

&#x1f3a5; 视频教程请关注 B 站&#xff1a;“嵌入式 Jerry” 一、前言 在嵌入式驱动开发中&#xff0c;“中断”几乎无处不在。无论是 GPIO 按键、串口通信、网络设备&#xff0c;还是 SoC 上的各种控制器&#xff0c;中断都扮演着核心触发机制的角色。对中断机制掌握程度…...

深入理解依赖、Jar 包与 War 包:Java 开发基石探秘

一、引言 在 Java 开发的广袤天地里&#xff0c;依赖管理如同建筑的基石&#xff0c;默默支撑着项目的稳定构建与运行。而 Jar 包和 War 包&#xff0c;作为 Java 应用的常见打包形式&#xff0c;各自承载着不同的使命。本文将深入探讨依赖的重要性&#xff0c;并清晰解说 Jar…...

01.Python代码Pandas是什么?pandas的简介

01.Python代码Pandas是什么&#xff1f;pandas的简介 提示&#xff1a;帮帮志会陆续更新非常多的IT技术知识&#xff0c;希望分享的内容对您有用。本章分享的是pandas的使用语法。前后每一小节的内容是存在的有&#xff1a;学习and理解的关联性&#xff0c;希望对您有用~ pyth…...

国产紫光同创FPGA实现SDI视频编解码+图像缩放,基于HSSTHP高速接口,提供2套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目本博已有的 SDI 编解码方案本方案在Xilinx--Artix7系列FPGA上的应用本方案在Xilinx--Kintex系列FPGA上的应用本方案在Xilinx--Zynq系列FPGA上的应用本方案在Xilinx--U…...

25.4.22学习总结

如何通过好友列表对聊天框的切换 首先&#xff0c;我们知道&#xff0c;你的好友列表是用ListView组件实现的&#xff0c;那么&#xff0c;接下来&#xff0c;我们将开始讲解如何实现切换。 一、改造数据结构 如果你是跟着我的上一篇文章做的话&#xff0c;应该需要修改一些的…...

Agent智能体ReAct机制深度解读:推理与行动的完美闭环

一、从Chain-of-Thought到ReAct的范式演进 1.1 传统决策机制的局限 #mermaid-svg-Jf3ygvgHcGciJvX8 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-Jf3ygvgHcGciJvX8 .error-icon{fill:#552222;}#mermaid-svg-Jf3y…...

UnityDots学习(四)

官方案例HelloCube和Tank学习研究&#xff1a; HelloCube: 通用部分&#xff1a; 使用Authoring根据Inspector的勾选添加为Entity添加不同Component。然后每个System会根据实体添加的Component运行不同的System逻辑。 1. MainThread 简单构造System 先看System接口定义&am…...

Debian 12.10 root 登录失败,两步解决!

大家好&#xff0c;这里是 DBA学习之路&#xff0c;专注于提升数据库运维效率。 前言 今天看到 debian 正式发布 12.10&#xff0c;安装完成后发现无法登录 root 用户&#xff1a; 这里我一开始怀疑是 root 密码错了&#xff0c;所以改了一下 root 密码&#xff0c;忘记 root …...

AI大模型:(二)2.3 预训练自己的模型

目录 1.预训练原理 2.预训练范式 1.未标注数据 2.标注数据 3.有正确答案、也有错误答案 3.手撕transform模型 3.1.transform模型代码 3.2.训练数据集 3.3.预训练 3.4.推理 4.如何选择模型 5.如何确定模型需要哪种训练 大模型预训练(Large-scale Pre-training…...

【C语言】初阶算法相关习题(一)

个人主页 文章目录 ⭐一、数字在升序数组中出现的次数&#x1f3e0;二、整数转换&#x1f680;三、至少是其他数字两倍的最大数&#x1f3dd;️四、字符个数的统计&#x1f384;五、自除数&#x1f3a1;六、除自身以外数组的乘积&#x1f389;七、两个数组的交集 ⭐一、数字在…...

WITH 临时表 SQL优化

在 SQL 优化中&#xff0c; 临时表&#xff08;CTE&#xff0c;Common Table Expression&#xff0c;公共表表达式&#xff09; 是一种强大的工具&#xff0c;它通过定义一个临时的结果集&#xff08;可以理解为 “虚拟表”&#xff09;&#xff0c;让复杂查询更易读、更高效&a…...

Go语言中 defer 使用场景及深度注意事项指南

文章精选推荐 1 JetBrains Ai assistant 编程工具让你的工作效率翻倍 2 Extra Icons&#xff1a;JetBrains IDE的图标增强神器 3 IDEA插件推荐-SequenceDiagram&#xff0c;自动生成时序图 4 BashSupport Pro 这个ides插件主要是用来干嘛的 &#xff1f; 5 IDEA必装的插件&…...

第33周JavaSpringCloud微服务 面试题

一、项目面试 面试中介绍项目的方法 在面试过程中&#xff0c;若被问及相关项目&#xff0c;为提升通过几率&#xff0c;应着重介绍项目的功能点和架构升级内容。确保将项目的亮点讲透讲精彩&#xff0c;这对获取 offer 至关重要。 1. 项目架构 项目整体架构概述 项目整体…...

鸿蒙开发:Swiper轮播图

鸿蒙Swiper组件详解 一、Swiper组件概述 Swiper是鸿蒙(HarmonyOS)系统中提供的一个滑动容器组件&#xff0c;它允许用户通过手指滑动来切换子组件&#xff08;通常是页面或图片&#xff09;&#xff0c;实现轮播图、引导页、图片浏览器等常见UI效果。 说明 该组件从API versi…...

Go语言之sync包 WaitGroup的使用和底层实现

在 Go 语言里&#xff0c;sync 包中的 WaitGroup 是一个实用工具&#xff0c;用于等待一组 goroutine 完成任务。其核心原理是通过内部维护一个计数器&#xff0c;该计数器初始值为 0&#xff0c;每启动一个新的 goroutine 就将计数器加 1&#xff0c;每个 goroutine 完成任务后…...

7N60-ASEMI无人机专用功率器件7N60

编辑&#xff1a;LL 7N60-ASEMI无人机专用功率器件7N60 型号&#xff1a;7N60 品牌&#xff1a;ASEMI 封装&#xff1a;TO-220F 最大漏源电流&#xff1a;7A 漏源击穿电压&#xff1a;600V 批号&#xff1a;最新 RDS&#xff08;ON&#xff09;Max&#xff1a;1.20Ω …...

SystemV-消息队列与责任链模式

一、SystemV 消息队列 1. 消息队列API Ftok 函数定义&#xff1a; key_t ftok(const char *pathname, int proj_id);函数作用&#xff1a; 获取唯一的key值标识符&#xff0c;用于标识系统V消息队列。参数解释&#xff1a; pathname&#xff1a;有效的文件路径&#xff08;需…...

Ubuntu与Linux的关系

Linux 是一个 操作系统内核。它是一个类 Unix 系统&#xff0c;免费、开源&#xff0c;许多不同的操作系统&#xff08;叫“发行版”&#xff09;都是基于 Linux 内核构建的。 Ubuntu 是一个 基于 Linux 内核的操作系统发行版。它是目前最流行、最易用的 Linux 发行版之一&…...

同时支持windows和Linux的NFC读写器web插件

一个网站集成了NFC读写器的功能&#xff0c;如何才能跨系统运行呢&#xff0c;既要在windows系统下正常运行&#xff0c;也需要在银河麒麟&#xff0c;统信UOS等信创系统下运行。 友我科技NFC读写器web插件很好的解决了这个问题&#xff0c;在客户端不仅支持windows系统&#x…...

突破AI检测边界:对抗技术与学术伦理的终极博弈

随着GPT-4、Claude等大模型的文本生成能力突破人类写作水平&#xff0c;AI检测工具与对抗技术的博弈已进入白热化阶段。本文深入解析基于对抗训练的文本风格混淆网络如何突破GPTZero最新防御体系&#xff0c;探讨OpenAI多模态内容溯源系统引发的技术升级&#xff0c;并针对学术…...

pg数据库删除自建表空间

1. tbs_sjzx已经创建&#xff08;略&#xff09; pg数据库删除自己创建表空间;--查看表空间相关表 SELECT * FROM pg_tablespace; SELECT relname FROM pg_class WHERE reltablespace (SELECT oid FROM pg_tablespace WHERE spcname tbs_sjzx); SELECT * FROM pg_tables WHE…...

C++ 学习指南

new 关键字 #include <iostream> using namespace std;int* func() {// 在堆区创建int* p new int(10); return p; }void test01(void) {int *p func();cout << *p << endl;cout << *p << endl;cout << *p << endl;delete p;// 这…...

Scribe: 一个非常方便的操作文档编写工具

在日常生活中&#xff0c;当我们需要指导别人使用一个软件/web应用时&#xff0c;我们常常需要按流程对工具进行操作&#xff0c;走一遍主要功能&#xff0c;然后针对每一步进行截图&#xff0c;并附上操作说明。往往这样一套流程走下来&#xff0c;就会花费很长的时间。那么有…...

数据结构与算法-顺序表应用

一.通讯录的创建 首先我们要理解的是通讯录本身就是以顺序表为底层的 只不过顺序表中的数组&#xff0c;这里我们是用结构体来替代&#xff0c;用来存储用户的信息 由于是通讯录的本质就是顺序表&#xff0c;所以顺序表的任何方法它都能套用 Contact.h: #pragma once #def…...

DeepSeek系列(5):助力数据分析

数据解读与可视化建议 在数据驱动的商业环境中,有效解读数据并将其转化为直观可视化结果至关重要。DeepSeek作为强大的AI助手,可以帮助您从海量数据中提取洞见并提供专业的可视化建议。 DeepSeek在数据解读中的优势 DeepSeek可以通过以下方式帮助您更高效地解读数据: 上下…...

虚幻基础:动画k帧

文章目录 动画k帧&#xff1a;调整骨骼的变换达到自己想要的效果步骤打开动画原始文件选中骨骼调整到目标变换添加关键帧时间&#xff1a;自动添加到停留的那一帧数值&#xff1a;自动填写为调整后的数值 注释数值与骨骼细节面板上的数值并不对应&#xff0c;但是同样的效果为什…...

使用 LlamaIndex Workflows 与 Elasticsearch

作者&#xff1a;来自 Elastic Jeffrey Rengifo 在本文中&#xff0c;你将学习如何利用 LlamaIndex Workflows 与 Elasticsearch 快速构建一个使用 LLM 的自过滤搜索应用程序。 LlamaIndex Workflows 提出了一种不同的方式来处理将任务拆分给不同 agent 的问题&#xff0c;它引…...

相对论大师-记录型正负性质BFS/图论-链表/数据结构

看到这一题我的第一个思路就是双向bfs 起点是a&#xff0c;终点还是a&#xff0c;但是flag是相反的&#xff08;“越”的方向&#xff09; tip1.可以用字典vis来存储flag 刚开始初始化时vissta,visend一个对应0、1 要求两个队列相…...

代理设计模式:从底层原理到源代码的详细解释

代理设计模式&#xff08;Proxy Pattern&#xff09;是一种结构型设计模式&#xff0c;它通过创建一个代理对象来控制对目标对象的访问。代理对象充当客户端和目标对象之间的中介&#xff0c;允许在不修改目标对象的情况下添加额外的功能&#xff08;如权限控制、日志记录、延迟…...

EasyRTC音视频实时通话:打造高清低延迟的远程会议新生态

一、项目背景​ 随着数字化办公的普及&#xff0c;远程会议成为企业、教育机构、政府部门等组织跨地域协作沟通的重要方式。传统远程会议系统在音视频质量、低延迟传输、多平台兼容性等方面存在不足&#xff0c;难以满足用户对高清、流畅、稳定会议体验的需求。EasyRTC作为一款…...

零基础上手Python数据分析 (21):图表选择困难症?常用可视化类型详解与应用场景指南

写在前面 —— 告别盲目绘图,理解图表语言,为你的数据找到最佳“代言人” 在前面几篇博客中,我们已经学习了使用 Matplotlib 和 Seaborn 这两大 Python 可视化利器来绘制各种图表。我们掌握了创建折线图、柱状图、散点图、箱线图等常用图表的技术。然而,仅仅知道 如何 绘…...

HarmonyOS Next 编译之如何使用多目标产物不同包名应用

引言 在日常的开发中涉及到多签名和多产物构建输出时手动切换签名文件和包名在开发中是容易出错且费时的一个操作&#xff0c;鸿蒙提供了自定义hvigor插件和多目标产物构建&#xff0c;那我们可以通过hvigor插件来动态修改不同项目配置所需要的代码&#xff0c;保证一套代码在…...

Oracle Database Resident Connection Pooling (DRCP) 白皮书阅读笔记

本文为“Extreme Oracle Database Connection Scalability with Database Resident Connection Pooling (DRCP)”的中文翻译加阅读笔记。觉得是重点的就用粗体表示了。 白皮书版本为March 2025, Version 3.3&#xff0c;副标题为&#xff1a;Optimizing Oracle Database resou…...

Sharding-JDBC 系列专题 - 第七篇:Spring Boot 集成与 Sharding-Proxy 简介

Sharding-JDBC 系列专题 - 第七篇:Spring Boot 集成与 Sharding-Proxy 简介 本系列专题旨在帮助开发者全面掌握 Sharding-JDBC,一个轻量级的分布式数据库中间件。本篇作为系列的第七篇文章,将重点探讨 Sharding-JDBC 与 Spring Boot 的集成,以及 Sharding-Proxy 的基本概念…...