一、简介
编写手机app时,有时需要使用文字转语音(text to speech)的功能,比如开车时阅读收到的短信、导航语音提示、界面中比较重要的信息通过语音强调、……等。
由于android自带的pico tts并不支持中文,所以要既能阅读中文文本,还能阅读英文文本,必须下载第三方提供的能说中文的语音包。
二、申请百度tts授权
本节以百度2016年2月29日发布的“离在线融合语音合成sdk_android 2.2.3版”为例说明用c#实现语音合成的基本用法。之所以选择百度语音合成来实现,是因为据百度官网声明,该开发包是“永久免费”的。网址如下:
http://yuyin.baidu.com/tts/
由于原来已经申请过mydemos的授权,所以再继续申请tts授权就比较简单了,申请和设置步骤如下。
1、申请授权
进入 http://yuyin.baidu.com/tts/ 的首页:
单击【立即使用】,进入“开通语音合成服务”的页面:
在下拉框中选择原来已经申请的某个应用,单击【下一步】,然后按提示操作,开通离线服务即可。
2、在bdmapv371bindinglib项目中转换jar文件
先通过 http://yuyin.baidu.com/tts/ 首页中的【相关下载】下载对应的开发包,然后再按下面的步骤操作。
1、将示例中的com.baidu.tts_2.2.3.20160229_359d952_release.jar、galaxy-v2.0.jar添加到jars文件夹下,如下图所示,然后将其【生成操作】属性全部设置为“embeddedjar”。
2、在metadata.xml文件中添加下面的语句:
1
|
<remove-node path= "/api/package[@name='com.baidu.tts.aop']/interface[@name='iproxyfactory']/method[@name='createproxied' and count(parameter)=0]" />
|
3、重新生成项目,此时应该无错误。
经过这3个步骤,就完成了tts的jar包导入和转换为.cs文件的过程。
3、在mydemos项目中添加.so文件
将tts相关的4个.so文件添加到mydemos项目的x86文件夹下,如下图所示,然后将其【生成操作】属性全部设置为“androidnativelibrary”。
4、将.dat文件添加到sd卡的baidutts文件夹下
具体添加办法见【常见问题解答】,这里不再截图。
也可以先将这些文件添加到assets文件夹下,然后通过代码将其复制到sd卡的文件夹下。为简化起见,这里通过手工直接复制了。
ok,经过上面这4步,以后就可以在mydemos项目中的任何模块中轻松利用百度tts实现语音阅读的功能了
三、示例
1、运行截图
单击【阅读】,就会自动用女音朗读文本框中的内容,单击【批量阅读】,就会依次朗读队列中添加的文字段(主要是为了演示阅读各种不同的中英文短句)。
2、设计步骤
(1)添加ch2005main.axml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
<?xml version= "1.0" encoding= "utf-8" ?>
<linearlayout xmlns:android= "http://schemas.android.com/apk/res/android"
android:layout_width= "match_parent"
android:layout_height= "match_parent"
android:orientation= "vertical" >
<linearlayout
android:layout_width= "fill_parent"
android:layout_height= "50dp"
android:orientation= "horizontal"
android:weightsum= "4" >
<button
android:id= "@+id/speak"
android:layout_width= "fill_parent"
android:layout_height= "fill_parent"
android:layout_weight= "1"
android:lines= "2"
android:text= "阅读"
android:textsize= "12dp" />
<button
android:id= "@+id/pause"
android:layout_width= "fill_parent"
android:layout_height= "fill_parent"
android:layout_weight= "1"
android:lines= "2"
android:text= "暂停"
android:textsize= "12dp" />
<button
android:id= "@+id/resume"
android:layout_width= "fill_parent"
android:layout_height= "fill_parent"
android:layout_weight= "1"
android:lines= "2"
android:text= "继续"
android:textsize= "12dp" />
<button
android:id= "@+id/stop"
android:layout_width= "fill_parent"
android:layout_height= "fill_parent"
android:layout_weight= "1"
android:lines= "2"
android:text= "停止"
android:textsize= "12dp" />
</linearlayout>
<linearlayout
android:layout_width= "fill_parent"
android:layout_height= "50dp"
android:orientation= "horizontal"
android:weightsum= "4" >
<button
android:id= "@+id/synthesize"
android:layout_width= "fill_parent"
android:layout_height= "fill_parent"
android:layout_weight= "1"
android:lines= "2"
android:text= "synthesize"
android:textsize= "12dp" />
<button
android:id= "@+id/play"
android:layout_width= "fill_parent"
android:layout_height= "fill_parent"
android:layout_weight= "1"
android:lines= "2"
android:text= "play"
android:textsize= "12dp" />
<button
android:id= "@+id/batchspeak"
android:layout_width= "fill_parent"
android:layout_height= "fill_parent"
android:layout_weight= "1"
android:lines= "2"
android:text= "批量阅读"
android:textsize= "12dp" />
<button
android:id= "@+id/nextactivity"
android:layout_width= "fill_parent"
android:layout_height= "fill_parent"
android:layout_weight= "1"
android:lines= "2"
android:enabled= "false"
android:text= "备用"
android:textsize= "12dp" />
</linearlayout>
<edittext
android:id= "@+id/input"
android:layout_width= "fill_parent"
android:layout_height= "wrap_content"
android:hint= "input" />
<textview
android:id= "@+id/showtext"
android:layout_width= "fill_parent"
android:layout_height= "fill_parent"
android:layout_margin= "10dp"
android:background= "@android:color/darker_gray"
android:minlines= "3"
android:scrollbars= "vertical" />
</linearlayout>
|
2、添加ch2005mainactivity.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
|
using system;
using system.collections.generic;
using system.linq;
using system.text;
using android.app;
using android.os;
using android.widget;
using com.baidu.tts.client;
using com.baidu.tts.answer.auth;
namespace mydemos.srcdemos
{
[activity(label = "【例20-5】百度tts基本用法" )]
public class ch2005mainactivity : activity, ispeechsynthesizerlistener
{
private edittext minput;
private textview mshowtext;
private speechsynthesizer mspeechsynthesizer;
/// <summary>
/// sd卡上保存百度tts文件的路径
/// </summary>
private string msampledirpath;
private const string speechfemalemodelname = "bd_etts_speech_female.dat" ;
private const string speechmalemodelname = "bd_etts_speech_male.dat" ;
private const string textmodelname = "bd_etts_text.dat" ;
private const string englishspeechfemalemodelname = "bd_etts_speech_female_en.dat" ;
private const string englishspeechmalemodelname = "bd_etts_speech_male_en.dat" ;
private const string englishtextmodelname = "bd_etts_text_en.dat" ;
protected override void oncreate(bundle savedinstancestate)
{
base.oncreate(savedinstancestate);
setcontentview(resource.layout.ch2005main);
msampledirpath = android.os.environment.externalstoragedirectory.path + "/baidutts" ;
console.writeline( "msampledirpath=" + msampledirpath);
initialview();
initialtts();
}
private void initialtts()
{
mspeechsynthesizer = speechsynthesizer.instance;
mspeechsynthesizer.setcontext( this );
mspeechsynthesizer.setspeechsynthesizerlistener( this );
// 文本模型文件路径 (离线引擎使用)
mspeechsynthesizer.setparam(speechsynthesizer.paramttstextmodelfile,
msampledirpath + "/" + textmodelname);
// 声学模型文件路径 (离线引擎使用)
mspeechsynthesizer.setparam(speechsynthesizer.paramttsspeechmodelfile,
msampledirpath + "/" + speechfemalemodelname);
// 请替换为语音开发者平台上注册应用得到的app id (离线授权)
//mspeechsynthesizer.setappid("your_app_id");
mspeechsynthesizer.setappid(ch.ttsappid);
// 请替换为语音开发者平台注册应用得到的apikey和secretkey (在线授权)
//this.mspeechsynthesizer.setapikey("your_api_key", "your_secret_key");
this .mspeechsynthesizer.setapikey(ch.ttsapikey, ch.ttssecretkey);
// 发音人(在线引擎),可用参数为0,1,2,3。。。(服务器端会动态增加,各值含义参考文档,以文档说明为准。0--普通女声,1--普通男声,2--特别男声,3--情感男声。。。)
mspeechsynthesizer.setparam(speechsynthesizer.paramspeaker, "0" );
// 设置mix模式的合成策略
mspeechsynthesizer.setparam(speechsynthesizer.parammixmode, speechsynthesizer.mixmodedefault);
// 授权检测接口(可以不使用,只是验证授权是否成功)
authinfo authinfo = this .mspeechsynthesizer.auth(ttsmode.mix);
if (authinfo.issuccess)
{
console.writeline( "授权检测--授权成功(auth success)。" );
}
else
{
string errormsg = authinfo.ttserror.detailmessage;
console.writeline( "授权检测--授权失败(auth failed),errormsg=" + errormsg);
}
// 初始化tts
mspeechsynthesizer.inittts(ttsmode.mix);
// 加载离线英文资源(提供离线英文合成功能)
int result = mspeechsynthesizer.loadenglishmodel(
msampledirpath +
"/" + englishtextmodelname, msampledirpath +
"/" + englishspeechfemalemodelname);
}
private void initialview()
{
minput = findviewbyid<edittext>(resource.id.input);
minput.text = "今天阳光明媚,风和日丽!" ;
mshowtext = findviewbyid<textview>(resource.id.showtext);
var speak = findviewbyid<button>(resource.id.speak);
speak.click += delegate
{
string text = this .minput.text;
int result = this .mspeechsynthesizer.speak(text);
if (result < 0 )
{
system.diagnostics.debug.writeline( "出错了,错误码:{0},请检查百度tts文档中对应错误码的含义。" , result);
}
};
var pause = findviewbyid<button>(resource.id.pause);
pause.click += delegate
{
mspeechsynthesizer.pause();
};
var resume = findviewbyid<button>(resource.id.resume);
resume.click += delegate
{
mspeechsynthesizer.resume();
};
var stop = findviewbyid<button>(resource.id.stop);
stop.click += delegate
{
mspeechsynthesizer.stop();
};
var synthesize = findviewbyid<button>(resource.id.synthesize);
synthesize.click += delegate
{
string text = this .minput.text;
int result = this .mspeechsynthesizer.synthesize(text);
if (result < 0 )
{
system.diagnostics.debug.writeline( "error,please look up error code in doc or url:http://yuyin.baidu.com/docs/tts/122 " );
}
};
var play = findviewbyid<button>(resource.id.play);
play.click += delegate { };
var batchspeak = findviewbyid<button>(resource.id.batchspeak);
batchspeak.click += delegate
{
list<speechsynthesizebag> bags = new list<speechsynthesizebag>();
bags.add(getspeechsynthesizebag( "123456" , "0" ));
bags.add(getspeechsynthesizebag( "你好" , "1" ));
bags.add(getspeechsynthesizebag( "使用百度语音合成sdk" , "2" ));
bags.add(getspeechsynthesizebag( "hello" , "3" ));
bags.add(getspeechsynthesizebag( "这是一个demo工程" , "4" ));
int result = this .mspeechsynthesizer.batchspeak(bags);
if (result < 0 )
{
system.diagnostics.debug.writeline( "error({0}),please look up error code in doc or url:http://yuyin.baidu.com/docs/tts/122 " , result);
}
};
}
protected override void ondestroy()
{
base.ondestroy();
}
private speechsynthesizebag getspeechsynthesizebag(string text, string utteranceid)
{
speechsynthesizebag speechsynthesizebag = new speechsynthesizebag();
speechsynthesizebag.settext(text);
speechsynthesizebag.utteranceid = utteranceid;
return speechsynthesizebag;
}
public void onerror(string utteranceid, speecherror error)
{
console.writeline( "onerror error=" + error.description + "--utteranceid=" + utteranceid);
}
public void onspeechfinish(string utteranceid)
{
console.writeline( "onspeechfinish utteranceid=" + utteranceid);
}
public void onspeechprogresschanged(string p0, int p1)
{
//console.writeline("onspeechprogresschanged");
}
public void onspeechstart(string utteranceid)
{
console.writeline( "onspeechstart utteranceid=" + utteranceid);
}
public void onsynthesizedataarrived(string utteranceid, byte [] data, int progress)
{
console.writeline( "onsynthesizedataarrived" );
}
public void onsynthesizefinish(string utteranceid)
{
console.writeline( "onspeechfinish utteranceid=" + utteranceid);
}
public void onsynthesizestart(string utteranceid)
{
console.writeline( "onsynthesizestart utteranceid=" + utteranceid);
}
}
}
|