百度语音合成离线使用研究

1 研究背景

1.1 百度语音合成概述

语音合成(Text to Speech)是将文本合成为语音,即声音文件。语音合成是实现人机语音交互,建立一个有听和讲能力的交互系统所必需的关键技术。随着语音技术的发展,百度自主研发了语音合成系统(TTS),功能是接受用户发送的文本,生成语音发送给用户。

1.2研究目的和意义

设备有用到语音提示,并且设备无连接网络条件,当前社会上能提供离线语音合成功能技术支持的产家如科大讯飞等都收费昂贵,而使用专业语音合成软件合成语音再应用到设备上未免较为麻烦。百度语音永久免费无疑能减轻公司的成本负担,同时开发应用时直接调用其接口进行合成功能,方便且提高工作效率。

1.3国内外研究现状

当前社会上只有对百度语音SDK1.0版本进行研究,而百度语音1.0版本只有在线合成功能无法满足我们的需求。百度语音SDK2.0版本于2016年4月5日发布,使用离在线融合技术,能够使用临时授权文件离线使用30天或者在其官网提交应用包名申请离线正式授权后,设备联网自动下载离线授权文件就可永久离线使用;另外,2.0版本还提供有停止语音合成,获取合成语音等功能。

 

2 项目主要研究内容

2.1 主要工作内容

       ①百度官网注册申请授权;

       ②基于百度语音SDK2.0版本封装编写语音播放和停止接口;

③编写服务端与客户端程序

       ④反编译百度语音so动态库和修改百度语音jar包

       ⑤封装jar包方便使用

2.2 具体实施方案

2.2.1 百度官网注册申请授权

       ① 登陆百度官网http://yuyin.baidu.com/ 注册开发者信息

       ②创建新应用

应用创建成功:

③获取key值

④开通服务

⑤ 申请离线合成授权

 

2.2.2程序开发研究

2.2.2.1 测试工程测试语音合成功能

首先新建工程VoiceTest,基于百度语音2.0版本封装语音播放和停止接口,检测百度语音合成的功能实现。

将百度语音SDK开发包中的libs目录中的armeabi和arme abi-v7a文件夹拷贝到工程libs目录,这两文件夹中包含各自平台的SO库,同时将galaxy_lite.jar和com.baidu.tts_2.2.5.20160425 _697b6b8_rel ease.jar拷贝到工程libs目录,gala xy_lite.jar是百度安卓公共基础库。将开发包中的data目录下的dat语音文件放到工程assets路径下,待源码一遍设置资源文件参数时使用。

在AndroidManifest.xml清单文件中,增加如下权限:

<uses-permissionandroid:name="android.permission.MODIFY_AUDIO_SETTINGS" />

    <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<uses-permissionandroid:name="android.permission.WRITE_SETTINGS" />

       工程源码中封装了语音合成与停止合成的接口,当调用语音合成时首先判断设备sd卡目录下是否有BaiduTTS文件夹且目录是否为空,若无该文件夹则创建文件夹同时将工程assets中的资源复制到该sd卡目录下,再进行Tts初始化与授权等参数设置,然后调用SDK包中的speak()方法进行语音合成。经测试文本变语音功能,中文、英文以及中英文播放,数字,美元,数则计算识别播放正常,音量能随系统音量设置变化。停止语音合成即为调用SDK开发包中的stop()方法,能够停止语音合成,即按即停。

 

2.2.2.2 应用key值配置文件

因语音合成功能在多个应用程序都有应用到,而每个应用皆需在百度官网申请授权,故设置配置文件保存各个应用包名和百度语音提供的App Id、API Id及密钥(每个应用的这三个参数都需百度语音提供,参数值不同),以便不同应用调用接口传参。

该配置文件使用键值对的方法判断应用名和各个键值对,如:

APP Name=VoiceTest|AppID:8056058|API Key:值|Secret Key:值;

APP Name=evaluator|App ID:8056058|API Key:值|Secret Key:值;

    该配置文件放在工程assets文件夹中,随语音文件等复制到设备sd卡的BaiduTTS目录中,当目录中有该配置文件时则将原先配置文件替换掉。

 

2.2.2.3 编写服务端客户端程序

因每个应用的语音合成使用都需百度语音提供App Id、API Id及密钥这三个参数,使用key值配置文件虽能实现功能,但考虑操作繁琐以及后期百度语音收费顾虑,故更改算法为封装一个百度语音合成的服务应用,其他应用如需实现语音合成功能则通过调用jar包传参给该服务应用进行合成播放。

服务端和客户端使用AIDL Service进行通信(2.3 研究的关键技术章节中会详细介绍)。服务端要将assets的语音资源文件复制到手机内部sd卡的BaiduTTS文件夹中,同时工程的libs中放置相关的语音合成SO动态库和jar包;服务端的服务源码中封装了语音合成与停止合成接口,以及Tts初始化与授权等参数设置。

 

2.2.2.4 封装客户端jar包

发现应用安装第一次使用时需联网自动下载离线授权文件后才能离线使用百度语音合成功能,而我之前都是联网安装好应用播放,语音能够合成,然后再断网语音也能合成所以没太注意说首次安装使用需联网。

在设备上没有找到联网下载的正式授权文件,所以考虑抓网络数据包查看联网时请求和响应。使用tcpdump抓包,wirshark分析log发现语音合成程序初次安装联网使用时,设备会向百度服务器(http://220.181.163.108:80/auth.php)发送请求,服务器返回cuid、sign、app、selfDef、sta五个键值对,由于不清楚百度语音的授权文件组成,试了都不行,只清楚临时授权文件是有授权日期并加密过。下图为以VoiceTest应用联网的抓包数据:

分析联网数据包无效后,考虑反编译百度语音提供的jar包和使用IDA Pro反汇编so动态库,尝试是否有方法能绕过授权步骤实现语音合成功能。我使用IDA Pro反汇编百度语音的so动态库成C代码,其中包含有语音合成和授权判断,包括加密解密等等,感觉如果要改动难度较大。使用Jd-Gui反编译百度语音jar包,jar包中有对4个so动态库的加载调用,获取语音合成结果和授权结果,并语音提示试用到期时间。

语音合成服务端程序使用临时授权文件,将程序应用分别安装在A10,I90,I90_V2.0设备上都能够实现语音合成功能,首次使用都会提示“百度语音试用服务 999天后到期”,考虑设备不联网同时断电系统时间重置不存在会过期问题。使用jclasslib工具修改了百度语音的jar包并重新打包成百度语音jar包,成功删除过期提示语音提示。

将客户端与服务端连接的代码独立出来放在一个类中,并将该类与客户端的AIDL文件以及gen目录下的AIDL生成的java文件一起打包成jar包,留出语音合成的接口startSpeech()与停止语音合成stopSpeech()的接口给客户端调用,客户端调用startSpeech()时需传Activity的上下文和要合成的内容,stopSpeech()不需要传值。

百度语音的客户端服务端在I90_V1.0/V2.0和A10设备上测试都能正常合成/停止语音。

 

2.3 研究的关键技术

2.3.1 AIDL实现服务端与客户端通信

Android中,如果我们需要在不同进程间实现通信,就需要用到AIDL技术去完成。

AIDL(Android Interface Definition Language)是一种接口定义语言,编译器通过*.aidl文件的描述信息生成符合通信协议的Java代码,我们无需自己去写这段繁杂的代码,只需要在需要的时候调用即可,通过这种方式我们就可以完成进程间的通信工作。

2.3.1.1 服务端程序

首先,我们需要建立一个服务端的工程,如图:

在ISpeechService.aidl中我们定义了开始语音合成与停止语音合成接口,如图:

在Eclipse插件的帮助下,编译器会自动在gen目录中生成对应的ISpeechService.java文件,格式化后的代码如下:

该文件的大纲视图:

ISpeechService接口中的抽象内部类Stub继承android.os.Binder类并实现ISpeechService接口,比较重要的方法是asInterface(IBinder)方法,该方法会将IBinder类型的对象转换成ISpeechService类型,必要的时候生成一个代理对象返回结果。

接下来就是我们的Service了:

我们实现了ISpeechService.Stub这个抽象类的startSpeech()和stopSpeech()方法,然后再onBind(Intent)方法中返回我们的stub实例,这样一来调用方获取的ISpeechService.Stub就是我们的这个实例,startSpeech和stopSpeech方法也会按照我们的期望那样执行。当然,要想让Service生效,我们还需要在AndroidManifest.xml中做一些配置工作:

服务端已经完成了,接下来我们就该完成客户端的工作了。

2.3.1.2 客户端程序

我已经建好了一个客户端工程,如图:

我们只需要把ISpeechService.aidl文件拷到相应的目录中即可,编译器同样会生成相对应的ISpeechService.java文件,这一部分和服务端没什么区别。这样一来,服务端和客户端就在通信协议上达到了统一。我们在SpeechClass中进行与服务端的服务连接,在 MainActivity中调用接口并传入上下文和字符串两个参数。

从代码中可以看到,我们要重写ServiceConnection中的onService Connected方法将IBinder类型的对像转换成我们的 ISpeechService类型。到现在我们就剩下最后一个步骤了,这个环节也是最为关键的,就是绑定我们需要的服务。我们通过服务端Service定义的 “com.centerm.speechservice”这个标识符来绑定其服务,这样客户端和服务端就实现了通信的连接,我们就可以调用ISpeechService中的语音合成与停止合成方法了。

2.3.1.3 封装jar包

将客户端中的AIDL包和gen中的AIDL包,以及与服务器连接的SpeechClass一起封装为jar包,方便客户端调用。

客户端调用jar包使用:

 

2.3.2 tcpdump网络抓包

下载tcpdump (手机要有root权限)

adb push D:\软件\反编译工具\android网络抓包\tcpdump /data/local/tcpdump

adb shell chmod 6755 /data/local/tcpdump

adb shell su root

cd /data/local

./tcpdump -i any -p -s 0 -w/sdcard/capture.pcap

命令参数:

       # "-i any": listen on any network interface

# "-p": disable promiscuous mode (doesn't workanyway)

# "-s 0": capture the entire packet

# "-w": write packets to a file (rather thanprinting to stdout)

... do whatever you want to capture, then ^C to stop it...

 adb pull /sdcard/capture.pcap d:/

在电脑上用wireshark打开D盘中capture.pcap即可分析log

 

2.3.3 jclasslib修改jar包class文件

下载安装jclasslib

用jd-gui打开com.baidu.tts_2.2.5.20160425_697b6b8_release.jar,找到要修改的class文件,即OfflineAuthNotificationInterceptor.class,在class文件中找到对应需要修改的代码所在的方法名,我这里包含提示信息的这段代码在该class的最后一个方法,因该jar包有代码混淆处理,所有方法名都一样,如下图:

用winrar把OfflineAuthNotificationInterceptor.class解压出来到C盘 (目录无所谓)

打开安装好了的 jclasslib bytecode viewer ,点击软件的 File->Open Class File打开刚解压出来的class文件,点击methods->getAllDataBas-> Code(methods是表示方法,getAllDataBase是刚在jd-gui里面找到的方法名,Code包含了getAllDataBase方法里所有的信息),找到"百度语音试用服务%d天后到期"和"百度语音试用服务已经到期,请及时更新授权"。

在第20和86行中找到。(后面以"百度语音试用服务%d天后到期"为例)

点击第 20行后面的 #5 会跳转到Constant Pool常量池的第5个常量

再点右边的 cp info #203 会跳转到第203个常量

这里能看到String: 百度语音试用服务%d天后到期。

⑦新建java工程Test来处理该class文件,工程中导入jclasslib安装目录文件夹下lib文件中的jclasslib-library.jar包,工程源码见附录一:修改jar包中class文件源码

 

注意"C:\\OfflineAuthNotificationInterceptor.class"是我存放class的目录,if(i == 203) 这里是刚我在第六步找到的常量序号, " "空字符串是我想修改的文字信息!运行Test.java 。

⑧把C盘刚修改后的OfflineAuthNotificationInterceptor.class替换掉解压开的jar包里边的OfflineAuthNotificationInterceptor.class,该jar包解压开的一级目录如图示:

将com整个文件夹拷贝到C盘根目录下。

⑨在console控制台中使用打jar包命令:

      jarcvf filename.jar foldername

⑩再使用jd-Gui打开生成的jar包,发现原来的提示语音变为空字符串

该jar包替换掉服务端工程调用百度语音的jar包,测试无过期语音提示。

 

3 结论

3.1 研究成果总结

        设备在无法联网条件下,能够离线进行语音合成。语音合成功能包括能够中文、英文以及中英文播放,数字,美元,数则计算识别播放,音量能随系统音量设置变化。停止语音合成接口能够停止语音合成,即按即停。

3.2 使用说明

①安装SpeakService.apk应用(即服务端)到设备。

②客户端程序加载SpeechService.jar包到工程,并调用其接口。

jar包中封装的语音合成接口函数:

     public static void startSpeech(ContextbaseContext,String text);

参数baseContext: 客户端Activity的上下文;

参数text:需要语音合成的字符串

jar包中封装的停止语音合成接口函数:

publicstatic void stopSpeech();

 

③客户端Activity添加import com.speech.SpeechClass;


有兴趣的可以到该目录下载详情:

http://download.csdn.net/download/p876643136/10270151

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值