天天看點

3個開源TTS(二)eSpeak的簡要分析使用

    繼續開源TTS分析,隻能說是給剛起步的人一點幫助了,畢竟不是專業做這一塊的。今天主要先簡單介紹TTS過程,然後以eSpeak的動态庫編譯使用,獲得wav檔案結束。

    前文介紹eSpeak是c語言寫的一個小型的、開放源碼的語音合成系統,支援多種語言,這裡包括漢語,甚至是粵語,可以看看他的部落格和演講【1】。在eSpeak的介紹裡特别強調了采用“formant synthesis”(共振峰)合成方法,是以簡單了解下TTS的一般過程,由于并非專業,難免有問題,請指出和諒解。

    TTS(Text to Speech),也稱為語音合成技術,一般分為三個步驟,語言學處理,韻律處理和聲學處理。語言學處理就是模拟人對自然語言的了解過程,完成文本規範化、分詞、文法分析和語義分析,使處理後的輸出能夠為計算機所了解,并在其中加入所需要的各種發音提示,包括數字、特殊詞彙、斷句停頓等;韻律處理則是在文本的了解基礎上,規劃出音段特征,如音高、音長和音強等,利用韻律标記系統語調、節奏和重音這些韻律特征;聲學處理則是在前兩部分提供的資訊基礎上,利用語音語料庫,統計與規則結合語境參數、聲學參數等資訊解決韻律的控制語音的合成。簡單來說就是模拟人朗讀的過程,從一段文本的輸入進行分詞、了解開始,結合人的朗讀規律、發聲特點對文本進行标記,作為參數在聲音輸出時産生模拟人聲停頓變化等的過程。eSpeak所說的共振峰合成就是最後的語音産生過程。共振峰合成是最典型的基于聲學模型的合成技術,其設計的理論基礎是語音産生的源-濾波理論,即來自肺部的氣流通過聲門後,被看成具有一定譜結構的聲源S(f),也是聲道濾波器的激勵信号,聲道被看成足一個線性的濾波器,其轉移凼數T(f)是口和鼻處的體積速度U(f)和聲源s(f)之比,由于實際上總是存離口腔有一段距離的地方得到語音p(f),是以必須考慮到口腔的輻射效應R(f)。(更加詳細的資料,自己查找)而我們需要知道共振峰模型受參數提取的影響很大,參數不準确會導緻合成音質下降。而波形拼接的合成聲音音質最好,但合成新的聲音需要事先錄制新的說話人的聲音,共振峰合成隻需在頻域對聲音作修改。這樣的修改包括;振峰偏移、帶寬修改、共振峰強度的修改和頻帶斜坡的修改。Klatt設計了一個串/并聯混合型共振峰合成器,可以設定多到八個共振峰.并有單獨的濾波器來模拟鼻腔和氣管的共振。對聲源可做調整或多種選擇,以模拟不同的嗓音。(摘自【2】【3】)

    從上面的描述過程大緻可以知道TTS的一般過程,而共振峰合成技術是一種語音合成技術中的參數合成方法,通過模拟人的發聲過程,建立發聲模型,利用對人的濁音源、元音源等聲源發聲進行參數提取、估計,供後期的共振模拟使用。可見像我這種非專業人士隻是知道會有大量的參數調整控制,看來想對此軟體進行發聲控制使用可以,修改部分規則、擴充語言、詞典需要大量時間,再進一步想要優化流暢度之類的,估計就非常困難了。那麼下面咱們說怎麼利用其提供的源碼包編譯Windows上的動态庫,并使用,Linux上的應該類似。

    本人的環境是Windows XP,使用VC6.0編譯,源碼包espeak-1.46.02-source,隻為了産生wav格式檔案,是以沒用到PortAudio庫,但是編譯指令行版本時是直接可以發聲的。編譯過程非常簡單,在源碼包下\platforms\windows\windows_dll檔案夾中有!ReadMe.txt檔案告知。從源碼包根目錄下的src檔案夾中拷貝源碼檔案到platforms\windows\windows_dll\src中,但不拷貝speak_lib.h  speech.h  StdAfx.h  stdint.h檔案。而該eSpeak産生的windows DLL庫提供的接口在speak_lib.h中定義看到。我的編譯遇到了'espeak_SetParameter' : redefinition; different linkage的重定義錯誤,隻要在定義出前添加ESPEAK_API即可。

   使用代碼如下,也可參看【4】,可以把word換成nihao試試。

#include <stdio.h>
#include "speak_lib.h" 
#pragma comment(lib, "espeak_lib.lib")

FILE *f_wave = NULL;

void Write4Bytes(FILE *f, int value)
{//=================================
// Write 4 bytes to a file, least significant first
	int ix;

	for(ix=0; ix<4; ix++)
	{
		fputc(value & 0xff,f);
		value = value >> 8;
	}
}

static int OpenWaveFile(const char *path, int rate)//rate 22050
//=================================================
{
	// Set the length of 0x7ffff000 for --stdout
	// This will be changed to the correct length for -w (write to file)
	static unsigned char wave_hdr[44] = {
		'R','I','F','F',0x24,0xf0,0xff,0x7f,'W','A','V','E','f','m','t',' ',
		0x10,0,0,0,1,0,1,0,  9,0x3d,0,0,0x12,0x7a,0,0,
		2,0,0x10,0,'d','a','t','a',  0x00,0xf0,0xff,0x7f};

	if(path == NULL)
		return(2);

	if(strcmp(path,"stdout")==0)
	{
#ifdef PLATFORM_WINDOWS
// prevent Windows adding 0x0d before 0x0a bytes
		_setmode(_fileno(stdout), _O_BINARY);
#endif
		f_wave = stdout;
	}
	else
		f_wave = fopen(path,"wb");

	if(f_wave != NULL)
	{
		fwrite(wave_hdr,1,24,f_wave);
		Write4Bytes(f_wave,rate);
		Write4Bytes(f_wave,rate * 2);
		fwrite(&wave_hdr[32],1,12,f_wave);
		return(0);
	}
	return(1);
}   //  end of OpenWaveFile


static void CloseWaveFile()
//=========================
{
   unsigned int pos;

   if((f_wave == NULL) || (f_wave == stdout))
      return;

   fflush(f_wave);
   pos = ftell(f_wave);

	fseek(f_wave,4,SEEK_SET);
	Write4Bytes(f_wave,pos - 8);

	fseek(f_wave,40,SEEK_SET);
	Write4Bytes(f_wave,pos - 44);


   fclose(f_wave);
   f_wave = NULL;

} // end of CloseWaveFile

int SynthCallback(short *wav, int numsamples, espeak_EVENT *events)
{
    // 你可以根據源碼程式裡編寫這部分代碼實作生成語音檔案功能
    int wavDataIndex = 0;
	if(wav == NULL)
		return 1;
	else{
		while(wav[wavDataIndex+numsamples] != NULL)
			wavDataIndex++;
		fwrite(wav,sizeof(short),numsamples,f_wave);
		return(0);
	}
}

void main() 
{
    // TODO: Add your control notification handler code here
    char word[] = "hello world!";
    espeak_Initialize(AUDIO_OUTPUT_SYNCHRONOUS, 0, ".", 0);
    espeak_SetSynthCallback(SynthCallback);   // 設定回調函數
    espeak_SetVoiceByName("en");
	OpenWaveFile("test.wav",22050);
    espeak_Synth(word, 100, 0, POS_CHARACTER, 0, espeakCHARS_UTF8, NULL ,NULL);
    espeak_Synchronize();
    espeak_Terminate();
	CloseWaveFile();
}
           

   注意拷貝espeak_lib.dll和espeak_lib.lib,還有就是源碼包中的espeak-data要拷貝到應用目錄下。其中代碼都是從eSpeak的指令行源程式中拷貝出來的,是以有些混亂請諒解。而提供的接口自己檢視speak_lib.h中的說明就懂意思了。

    轉載請注明:http://blog.csdn.net/w7849516230/article/details/8243291,有問題或交流郵箱[email protected]

參看

【1】開源中國 黃冠能部落格http://my.oschina.net/hgneng

【2】語音合成技術的研究與發展-黃南川等

【3】共振峰語音合成算法研究和實作-趙亮

【4】跨平台TTS eSpeak Windows開發 http://www.cnblogs.com/hicjiajia/archive/2011/02/02/1948882.html

繼續閱讀