TTS中英文混合朗读的完全设计实现

来源:互联网 发布:saycam深孔钻编程 编辑:程序博客网 时间:2024/06/11 01:35


中英文混合朗读一直是个难点,即在一段文本中要将中文和英文分离出来进行分别朗读,又不能打乱朗读的次序,所以我们设计如下的两种方法,每种方法都有各自的优点和缺点。

 

①采用同步朗读方式进行混合朗读

 

将朗读文本进行提取分析,提取本文本中的中文和英文,在编程中,我们设定两个标志:

const int IsEnglish = 0; // 英文标志

const int IsChinese = 3; // 中文标志

标点符号和数字默认和它前面的字的标志一致。当处理文本时标志发生改变时,立即调用语音合成引擎进行朗读。

//循环遍历每个字

for (int i = 0; i < length; i++)

{

//提取字母

current = m_strTxt.GetAt(i);

// 如果是英文字母,标志设为0

if((current >= 'A' && current <= 'Z') || (current >= 'a' && current <= 'z'))

{

CFlag = IsEnglish; // 如果是英文字母,标志设为0

}

else if((current >=0&&current<= 64)||(current >=91&&

current<= 96)||(current >=123&&current<= 127))

//如果是数字则和它的前一个字母的标志一样

{

if(PFlag==IsEnglish)

CFlag=IsEnglish;//英文标志

else

CFlag=IsChinese;//中文标志

}

else

{

CFlag = IsChinese;//中文标志

}

if (CFlag == PFlag) //若当前标志与前一个字母标志相同则个数增一

iCount++;

else 

if (iCount!=0) //字符类型变化,读出前面的文本 

if(PFlag==IsEnglish)

SetVoice(szwEnglish);//将引擎设置成英文引擎

else 

SetVoice(szwChinese);//将引擎设置成中文引擎

str = m_strTxt.Mid(first,iCount);//提取出标志相同的文字

//设置成同步朗读方式,将文本同步朗读出来

hr = m_IpVoice->Speak(str.AllocSysString(), 0, 0 );

}

PFlag = CFlag;//改变当前标志

first = i;//设置当前位置

iCount = 1;//个数还原为1

}

//读出最后一段文本

if(PFlag==IsEnglish)

SetVoice(szwEnglish);//将引擎设置成英文引擎

else 

SetVoice(szwChinese);//将引擎设置成中文引擎

str = m_strTxt.Mid(first,length);

//设置成同步朗读方式,将文本同步朗读出来

hr = m_IpVoice->Speak(str.AllocSysString(), 0, 0 );


 

采用异步XML格式进行混合朗读

 

将第二种方式与第一中方式相似,区别只是在于它是将文本进一步进行处理,最后生成符合XML朗读格式的文本后对文本进行朗读,而不是调用当标志发生改变时立即调用语音合成引擎进行朗读:

//设置XML起始标志

strEngine.printf("<xml version=/"1.0/">/n<SAPI>/n

<VOICE REQUIRED=/"NAME=%s/">",m_strZHEngine.c_str());

strWord+=strEngine;

for (int i = 1; i <= nLength; i++)

{

...... 

if (iCount!=0) //字符类型变化,进行文本处理

{

if(PFlag==IsChinese)//中文标志

{

//将中文朗读文本XML格式插入文本中

strWord+="</VOICE>/n";

strEngine.printf("

<VOICE REQUIRED=/"NAME=%s/">",m_strZHEngine.c_str());

strWord+=strEngine;

strWord+=strTemp.SubString(first,iCount);

}

else

{

//将英文朗读文本XML格式插入文本中

strWord+="</VOICE>/n";

strEngine.printf(

"<VOICE REQUIRED=/"NAME=%s/">",m_strENEngine.c_str());

strWord+=strEngine;

strWord+=strTemp.SubString(first,iCount);

}

}

......

}

//读出最后一段文本

if(PFlag==IsChinese)

{

strWord+="</VOICE>/n";

strEngine.printf("

<VOICE REQUIRED=/"NAME=%s/">",m_strZHEngine.c_str());

strWord+=strEngine;

strWord+=strTemp.SubString(first,iCount);

//添加xml结束标志

strEngine="</VOICE>/n</SAPI>/n</xml>";

       strWord+=strEngine;

}

else

{

//将英文朗读文本XML格式插入文本中

strWord+="</VOICE>/n";

strEngine.printf("

<VOICE REQUIRED=/"NAME=%s/">",m_strENEngine.c_str());

strWord+=strEngine;

strWord+=strTemp.SubString(first,iCount);

//添加xml结束标志

strEngine="</VOICE>/n</SAPI>/n</xml>";

strWord+=strEngine;

}

//采用XML和异步朗读方式朗读文本

TTS_Speak(strWord.c_str(),IS_MIX_SPEECH|IS_ASY_SPEECH);

}

   当然上面的两种方式各有各的优缺点,第一种方式处理较快,但是不能进行异步朗读,同步朗读时不能进行其它操作,而且频繁调用语音引擎进行声音设置容易造成内存占有率较高。第二种方法需要对文本进行插入处理比较耗时,且要求开发者要对XML语音朗读格式有一定的了解所以开发比较复杂,但它是可以进行异步朗读,使你在朗读的时候还能进行其他操作,且采用XML朗读方式,使你不用人为的去改变引擎的声音,将操作交给系统进行处理摆脱了频繁改变声音后造成的内存占有率较高的问题!

具体源码如下:


 

①采用VC开发的:

////////////////////////////////////////////////////////////////////////////////////////////
//函数  名:HRESULT CText2Speech::MixSpeech(char *speech)
//编写  者:南才北往
//参考资料: Speech SDK5.1 帮助文档
//函数功能:语音合成朗读--混合朗读.
//输入参数:char* speech - 文本朗读内容
//返回  值:S_OK - 成功 ; E_FAIL -失败
//备    注: 无
///////////////////////////////////////////////////////////////////////////////////////////

HRESULT CText2Speech::MixSpeech(char *speech)
{
    long length;
    HRESULT hr;
    CString  m_strTxt=speech,str;
    char current;
    int PFlag, CFlag;
    int iCount, first;
    const int IsEnglish = 0; // 英文标志
    const int IsChinese = 3; // 中文标志

    length = m_strTxt.GetLength();
    PFlag = IsChinese;
    iCount=0;
    first=0;
   
    for (int i = 0; i < length; i++)
    {
        current = m_strTxt.GetAt(i);
        if((current >= 'A' && current <= 'Z') || (current >= 'a' && current <= 'z'))
        {
            CFlag = IsEnglish; // 如果是英文字母,标志设为0
        }
        else if((current >=0&&current<= 64)||(current >=91&&current<= 96)||(current >=123&&current<= 127))//如果是数字则和它的前一个字母的标志一样
        {
            if(PFlag==IsEnglish)
                CFlag=IsEnglish;
            else
                CFlag=IsChinese;
        }
        else
        {
            CFlag = IsChinese;
        }

        if (CFlag == PFlag)
            iCount++;
        else
        {
            if (iCount!=0) //字符类型变化,读出前面的文本
            {
                if(PFlag==IsEnglish)
                    SetVoice(szwEnglish);
                else
                    SetVoice(szwChinese);
                str = m_strTxt.Mid(first,iCount);
                hr = m_IpVoice->Speak(str.AllocSysString(), 0, 0 );       
            }
            PFlag = CFlag;
            first = i;
            iCount = 1;
        }
    }
    //读出最后一段文本
    if(PFlag==IsEnglish)
        SetVoice(szwEnglish);
    else
        SetVoice(szwChinese);
    str = m_strTxt.Mid(first,length);
    hr = m_IpVoice->Speak(str.AllocSysString(), 0, 0 );
    return hr;
}

②采用c++builder开发的


////////////////////////////////////////////////////////////////////////////////////////////
//函数  名
void __fastcall TForm1::MixSpeech()
//编写  者:南才北往
//参考资料: Speech SDK5.1 帮助文档
//函数功能:语音合成朗读--混合朗读.
//输入参数:
Edit1->Text- 文本朗读内容
//返回  值:无
//备    注: 无
///////////////////////////////////////////////////////////////////////////////////////////

void __fastcall TForm1::MixSpeech()
{
    int nLength;
    char chCurrent;
    AnsiString strTemp,strWord;
    AnsiString strEngine;
//语音引擎名称

    strTemp=Edit1->Text;
    nLength=strTemp.Length();
    int PFlag, CFlag;
    int iCount, first;
    const int IsEnglish = 0; // 英文标志
    const int IsChinese = 3; // 中文标志
    PFlag = IsChinese;
    iCount=0;
    first=1;
    strEngine.printf("<xml version=/"1.0/">/n<SAPI>/n<VOICE REQUIRED=/"NAME=%s/">",m_strZHEngine.c_str());
    strWord+=strEngine;
    for (int i = 1; i <= nLength; i++)
    {
        chCurrent=strTemp.operator [](i);
        if((chCurrent >= 'A' && chCurrent <= 'Z') || (chCurrent >= 'a' && chCurrent <= 'z'))
        {
            CFlag = IsEnglish; // 如果是英文字母,标志设为0
        }
        else if((chCurrent >=0&&chCurrent<= 64)||
                (chCurrent >=91&&chCurrent<= 96)||
                (chCurrent >=123&&chCurrent< 127))//如果是数字则和它的前一个字母的标志一样
        {
            if(PFlag==IsEnglish)
                CFlag=IsEnglish;
            else
                CFlag=IsChinese;
        }
        else
        {
            CFlag = IsChinese;
        }

        if (CFlag == PFlag)
            iCount++;
        else
        {
            if (iCount!=0) //字符类型变化,读出前面的文本
            {
                if(PFlag==IsChinese)
                {
                    strWord+="</VOICE>/n";
                    strEngine.printf("<VOICE REQUIRED=/"NAME=%s/">",m_strZHEngine.c_str());
                    strWord+=strEngine;
                    strWord+=strTemp.SubString(first,iCount);
                }
                else
                {
                    strWord+="</VOICE>/n";
                    strEngine.printf("<VOICE REQUIRED=/"NAME=%s/">",m_strENEngine.c_str());
                    strWord+=strEngine;
                    strWord+=strTemp.SubString(first,iCount);
                }
            }
            PFlag = CFlag;
            first = i;
            iCount = 1;
        }
    }
    //读出最后一段文本
    if(PFlag==IsChinese)
    {
        strWord+="</VOICE>/n";
        strEngine.printf("<VOICE REQUIRED=/"NAME=%s/">",m_strZHEngine.c_str());
        strWord+=strEngine;
        strWord+=strTemp.SubString(first,iCount);
        strEngine="</VOICE>/n</SAPI>/n</xml>";
        strWord+=strEngine;
    }
    else
    {
        strWord+="</VOICE>/n";
        strEngine.printf("<VOICE REQUIRED=/"NAME=%s/">",m_strENEngine.c_str());
        strWord+=strEngine;
        strWord+=strTemp.SubString(first,iCount);
        strEngine="</VOICE>/n</SAPI>/n</xml>";
        strWord+=strEngine;
    }
    TTS_Speak(strWord.c_str(),IS_XML_SPEECH|IS_ASY_SPEECH);
    //ShowMessage(strWord);
}


 

原创粉丝点击