cn_tts.c 10.7 KB
/*
* 语音合成(Text To Speech,TTS)技术能够自动将任意文字实时转换为连续的
* 自然语音,是一种能够在任何时间、任何地点,向任何人提供语音信息服务的
* 高效便捷手段,非常符合信息时代海量数据、动态更新和个性化查询的需求。
*/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <malloc.h>


#include "qtts.h"
#include "msp_cmn.h"
#include "msp_errors.h"
#include "JZsdkLib.h"

#include "Megaphone_InputAndOutput.h"
#include "../../Music/Megaphone_Music.h"
#include "cn_tts.h"

struct t_Megaphone_tts_param
{
	int tts_tone;		//喊话器音色
	int tts_volume;		//tts原音量
	int tts_speed;		//喊话速度
	int tts_ReplyFlag;   //是否需要回复
	char ttsdata[1024]; //tts数据
};

typedef int SR_DWORD;
typedef short int SR_WORD ;
const char*  TTS_ID    = NULL;
const char*  TTS_ID1    = NULL;
const char*  TTS_ID2    = NULL;
static int TTSLoopPlayMode;
extern int tts_add;			//追加声音

static int TTS_Play_flag = 0; //TTS播放标志位 用于暂停播放tts
static int TTS_Finsh_Flag = JZ_FLAGCODE_OFF;
static void *TTS_flie(void *arg);


T_JZsdkReturnCode TTS_cnTTS(int name,int volume,int speed,int len,char *data, int ReplyFlag)
{
	pthread_t TTS_flie_task;
	pthread_attr_t task_attribute; //线程属性
	pthread_attr_init(&task_attribute);  //初始化线程属性
	pthread_attr_setdetachstate(&task_attribute, PTHREAD_CREATE_DETACHED);      //设置线程属性


	//tts参数结构体
	struct t_Megaphone_tts_param *Megaphone_tts_param = (struct t_Megaphone_tts_param *)malloc(sizeof(struct t_Megaphone_tts_param));
	if(Megaphone_tts_param == NULL)//如果申请内存失败,malloc会返回一个NULl
	{
		printf("malloc use failure");
		return 1;//申请内存失败,错误退出
	}

	//清空参数
	memset (Megaphone_tts_param,0,sizeof(struct t_Megaphone_tts_param));

	//写入参数
	Megaphone_tts_param->tts_speed = speed;
	Megaphone_tts_param->tts_tone =  name;
	Megaphone_tts_param->tts_volume = volume;
	Megaphone_tts_param->tts_ReplyFlag = ReplyFlag;
	snprintf(Megaphone_tts_param->ttsdata,len+8,"%s", data);
	

	int tts_ret = pthread_create(&TTS_flie_task,&task_attribute,TTS_flie,(void *)Megaphone_tts_param);		//TTS线程
	if(tts_ret != 0)
	{
		printf("创建TTS线程失败!\n");
		return -1;
	}

}

//TTS合成线程
static void *TTS_flie(void *arg)
{
	//设置标志位
	TTS_Finsh_Flag = JZ_FLAGCODE_ON;
	//打开TTS播放标志
	Set_TTS_play_flag(JZ_FLAGCODE_ON);

	struct t_Megaphone_tts_param *Megaphone_tts_param = (struct t_Megaphone_tts_param *) arg;

	//生成tts播放文件
	play_tts(Megaphone_tts_param->tts_tone, Megaphone_tts_param->tts_volume,  Megaphone_tts_param->tts_speed, Megaphone_tts_param->ttsdata, Megaphone_tts_param->tts_ReplyFlag);

	//释放掉结构体和标志位
	free(Megaphone_tts_param);
	Set_TTS_play_flag(JZ_FLAGCODE_OFF);
	TTS_Finsh_Flag = JZ_FLAGCODE_OFF;
}


/* 文本合成 */
int text_to_speech(const char* src_text, const char* des_path, const char* params)
{
	int          ret          = -1;
	const char*  sessionID    = NULL;
	const char*  sessionID1    = NULL;
	const char*  sessionID2    = NULL;
	unsigned int audio_len    = 0;
	int          synth_status = MSP_TTS_FLAG_STILL_HAVE_DATA;

	ret= -1;
	if (NULL == src_text || NULL == des_path)
	{
		printf("params is error!\n");
		return ret;
	}
	/* 开始合成 */	
	sessionID = QTTSSessionBegin(params, &ret);
	TTS_ID = sessionID;
	if (MSP_SUCCESS != ret)
	{
		printf("QTTSSessionBegin failed, error code: %d.\n", ret);
		return ret;
	}
	ret = QTTSTextPut(sessionID, src_text, (unsigned int)strlen(src_text), NULL);
	if (MSP_SUCCESS != ret)
	{
		printf("QTTSTextPut failed, error code: %d.\n",ret);
		QTTSSessionEnd(sessionID, "TextPutError");
		return ret;
	}
	printf("正在合成 ...\n");
	while (TTS_Play_flag) 
	{
		/* 获取合成音频 */
		const char* data = QTTSAudioGet(sessionID, &audio_len, &synth_status, &ret);
		if (MSP_SUCCESS != ret)
			break;
		//printf("获取成功,长度位%d\n",audio_len);
		if (NULL != data)
		{			
			Megaphone_RawPlay(16000, (unsigned char *)data,(int)audio_len);//播放合成的音频文件
			//printf("播放成功\n");
		}
		//else{printf("data==null\n");}
		
		if (MSP_TTS_FLAG_DATA_END == synth_status)
			break;
	}
	printf("\n");
	if (MSP_SUCCESS != ret)
	{
		printf("QTTSAudioGet failed, error code: %d.\n",ret);
		QTTSSessionEnd(sessionID, "AudioGetError");
		return ret;
	}
	ret = QTTSSessionEnd(sessionID, "Normal");
	if (MSP_SUCCESS != ret)
	{
		printf("QTTSSessionEnd failed, error code: %d.\n",ret);
	}
	return ret;
}

void login_tts(void){
	int         ret                  = MSP_SUCCESS;
	int error_num=0;
	const char* login_params         = "appid = 03857dfd, work_dir = .";//登录参数,appid与msc库绑定,请勿随意改动
relogin:
	/* 用户登录 */
	ret = MSPLogin(NULL, NULL, login_params); //第一个参数是用户名,第二个参数是密码,第三个参数是登录参数,用户名和密码可在http://www.xfyun.cn注册获取
	if (MSP_SUCCESS != ret&&error_num<10)
	{
		error_num+=1;
		printf("TTS Login failed, error code: %d num %d.\n", ret,error_num);
		goto relogin ;//登录失败,重新登录
	}
	else{
		printf("TTS login\n");
	}
	return;	
}





int play_tts(int name,int volume,int speed, char *argv, int ReplyFlag)
{
	int         ret                  = MSP_SUCCESS;
	char name_tts[16];
	char name_path[64];
	char session_begin_params[512];
	int tts_speed_add=50;
	int audio_volume = Music_get_music_volume();

	tts_speed_add=speed;

	/*
	* rdn:           合成音频数字发音方式
	* volume:        合成音频的音量
	* pitch:         合成音频的音调
	* speed:         合成音频对应的语速
	* voice_name:    合成发音人
	* sample_rate:   合成音频采样率
	* text_encoding: 合成文本编码格式
	*
	*/
	switch(name){
		case 0x01://中文女生
			snprintf(name_tts,16, "xiaoyan");
			snprintf(name_path,64, "fo|/root/tts/xiaoyan.jet");
			break;
		case 0x02://中文男生
			snprintf(name_tts,16, "xiaofeng");
			snprintf(name_path,64, "fo|/root/tts/xiaofeng.jet");
			break;
		case 0x11://英文女生
			snprintf(name_tts,16, "catherine");
			snprintf(name_path,64, "fo|/root/tts/catherine.jet");
			break;
		case 0x12://英文男生
			snprintf(name_tts,16, "john");
			snprintf(name_path,64, "fo|/root/tts/john.jet");
			break;
		case 0x31://东北
			snprintf(name_tts,16, "xiaoqian");
			snprintf(name_path,64, "fo|/root/tts/xiaoqian.jet");
			break;
		case 0x32://四川
			snprintf(name_tts,16, "xiaorong");
			snprintf(name_path,64, "fo|/root/tts/xiaorong.jet");
			break;
		case 0x33://河南
			snprintf(name_tts,16, "xiaokun");
			snprintf(name_path,64, "fo|/root/tts/xiaokun.jet");
			break;
		case 0x34://湖南
			snprintf(name_tts,16, "xiaoqiang");
			snprintf(name_path,64, "fo|/root/tts/xiaoqiang.jet");
			break;	
		case 0x35://陕西
			snprintf(name_tts,16, "xiaoying");
			snprintf(name_path,64, "fo|/root/tts/xiaoying.jet");
			break;	
		case 0x36://广东
			snprintf(name_tts,16, "xiaomei");
			snprintf(name_path,64, "fo|/root/tts/xiaomei.jet");
			break;				
		default://默认中文女生
			snprintf(name_tts,16, "xiaoyan");
			snprintf(name_path,64, "fo|/root/tts/xiaoyan.jet");
			break;
	}
	const char* filename             = "/root/record/1.wav"; //合成的语音文件名称      tts_res_path = fo|res/tts/xiaoyan.jet;fo|res/tts/common.jet,
	snprintf(session_begin_params,512,"engine_type = local,voice_name=%s, text_encoding = UTF8, tts_res_path = %s;fo|/root/tts/common.jet, sample_rate = 16000, speed = %d, volume = %d, pitch = 50, rdn = 2",name_tts,name_path,speed,volume);
	printf("session_begin_params=%s\n",session_begin_params);
	printf("argv=%s\n",argv);
	char tts_add1[512];
	char tts_add2[512];
	snprintf(tts_add1,512,"engine_type = local,voice_name=xiaoyan, text_encoding = UTF8, tts_res_path = fo|/root/tts/xiaoyan.jet;fo|/root/tts/common.jet, sample_rate = 16000, speed = %d, volume = 100, pitch = 50, rdn = 2",tts_speed_add);
	snprintf(tts_add2,512,"engine_type = local,voice_name=xiaofeng, text_encoding = UTF8, tts_res_path = fo|/root/tts/xiaofeng.jet;fo|/root/tts/common.jet, sample_rate = 16000, speed = %d, volume = 100, pitch = 50, rdn = 2",tts_speed_add);
	
	/* 文本合成 */
	printf("开始合成 ...\n");

	//修改音量
	Music_SetTTSvolume();

	//打开功放
	Megaphone_set_amplifier(1);

Start_TTS:
	ret = text_to_speech(argv, filename, session_begin_params);
	if (MSP_SUCCESS != ret)
	{
		printf("text_to_speech failed, error code: %d.\n", ret);
	}
	printf("合成完毕\n");
	if(tts_add==1){
		ret = text_to_speech(argv, filename, tts_add1);
		if (MSP_SUCCESS != ret)
		{
			printf("text_to_speech failed, error code: %d.\n", ret);
		}
		printf("追加女声合成完毕\n");
	}
	else if(tts_add==2){
		ret = text_to_speech(argv, filename, tts_add2);
		if (MSP_SUCCESS != ret)
		{
			printf("text_to_speech failed, error code: %d.\n", ret);
		}
		printf("追加男声合成完毕\n");
	}

	int time = 0;

	//如果循环播放标志被打开, 且处于TTS播放模式
	if(TTSLoopPlayMode==1 && TTS_Play_flag ==1)
	{
		while ((TTSLoopPlayMode==1) && (TTS_Play_flag ==1) && (time <=500))
		{
			delayMs(10);
			time +=10;
		}
		
		goto Start_TTS;
	}

	time = 0;

	//1倍速以下时,适当增加延时时间
	if (speed < 50)
	{
		//增益
		time = strlen(argv) *60*(speed*(100-speed))/50/50;
	}
	else 
	{
		time = strlen(argv) *60*speed/50;
	}
		
	//如果处于TTS播放模式下 进行正常的TTS延时
	int stop_delay = 0;
	if (time < 4000)
	{
		printf("延时长度%d 字节长度%d \n",time, strlen(argv) );
	}
	else
	{
		printf("延时长度4000" );
		time = 4000;
	}

	while (TTS_Play_flag == 1 && stop_delay <time)
	{
		stop_delay += 10;
		delayMs(10);
	}

	//关闭功放
	Megaphone_set_amplifier(JZ_FLAGCODE_OFF);

	//恢复音量
	Megaphone_Set_Volume(audio_volume);

	//如果拥有回复标识
	if (ReplyFlag == 1)
	{	
		//回复播放状态结束
		Megaphone_Reply_PlayStatus_end();
	}
	
	return 0;
}

void Set_TTS_play_flag(int flag)
{
	TTS_Play_flag = flag;
}

T_JZsdkReturnCode TTS_ttsPlayStop()
{
    //关闭flag
    Set_TTS_play_flag(JZ_FLAGCODE_OFF);

    //等待opus播放结束
    while (TTS_Finsh_Flag == JZ_FLAGCODE_ON)
    {
        delayMs(1);
    }
}


int TTS_Get_TTS_play_flag()
{
	return TTS_Play_flag;
}

int TTS_Set_TTSLoopPlayMode(int mode)//设置播放模式,mode==1单曲播放。mode==2单曲循环
{
	TTSLoopPlayMode=mode;
}