替换语音服务
Cruzr 提供了较多的系统服务,其中就包含了语音服务。当 Cruzr 提供的语音服务功能无法满足需求时,开发者可以使用自己实现的服务替换原有的服务。
语音服务中语音识别、自然语言处理和语音合成三个模块可以单独替换。
添加依赖库
引入Cruzr SDK
Cruzr SDK 下载引入请参考 创建第一个机器人应用 。
引入Cruzr_speech_override.aar
语音服务替换需要额外的 SDK (cruzr_speech_override.aar),将下载好的依赖包放入 cruzr-libs目录下,并在 app/build.gradle 添加以下依赖:
dependencies { implementation(name: 'cruzr_speech_override', ext: 'aar')//语音服务替换 SDK }
最后在 Android Studio 工具条上找到 Sync Project with Gradle Files按钮,点击执行同步操作即可。
注: 语音服务替换同时需要 cruzr.jar 和cruzr_speech_override.aar, 两者的依赖方式有区别。
配置替换规则
替换后的语音服务,在指定语言下,支持自动切换,在 app/src/main/res/xml 目录中创建 decision_list.xml 文件,添加覆盖配置,用于替换语音服务。当系统语言切换至指定语言时,语音服务指定模块将被替换, 使用开发者开发的语音服务模块。
<?xml version="1.0" encoding="utf-8"?> <decision-list> <decision> <if> <!-- 选择在什么语言下替换语言服务 --> <language name="en-rUS" /> </if> <then> <route-service> <!-- 替换语音合成 --> <parameter-group package="你的应用包名" service="speech.synthesis" /> <!-- 替换语音识别 --> <parameter-group package="你的应用包名" service="speech.recognition" /> <!-- 替换语义理解 --> <parameter-group package="你的应用包名" service="speech.understanding" /> </route-service> </then> <if> <!-- 选择在什么语言下替换语言服务 --> <language name="zh-rCN" /> </if> <then> <route-service> <!-- 替换语音合成 --> <parameter-group package="你的应用包名" service="speech.synthesis" /> </route-service> </then> </decision> </decision-list>
注: 语音服务三个模块可以独立替换其中的一个或者多个。其中语言配置必须按照 Android 资源标准语言格式配置。
开发自定义语音服务
实现服务
既然是要替换原有的语音服务,那么接下来,我们就要自己实现语音服务的功能。将服务替换规则配置好后,需要将配置中的服务进行具体实现。下面以语音播报为例:
public class YourSynthesisServices extends AbstractSynthesizer { private GoogleSpeaker googleSpeaker; public YourSynthesisServices() { googleSpeaker = new GoogleSpeaker(); } @Override protected void startSynthesizing(SynthesisOption synthesisOption) { // 新的语音播报实现 googleSpeaker.speak(synthesisOption.getInputText(), new TtsCallBack() { @Override public void onSpeakStart() { // 播报开始,通知进度变化 reportSynthesizingProgress(new SynthesisProgress.Builder(SynthesisProgress.PROGRESS_BEGAN).build()); } @Override public void onSpeaking() { // 播报中,通知进度变化 reportSynthesizingProgress(new SynthesisProgress.Builder(SynthesisProgress.PROGRESS_PLAYING)// 播报状态 .setRemainingTimeMillis(3000)// 剩余播报时间 .setPlayProgress(50)// 当前播报进度 .build()); } @Override public void onSpeakError(Exception e) { // 播报异常 rejectSynthesizing(new SynthesisException(SynthesisException.CODE_INTERNAL_ERROR)); } @Override public void onSpeakCompleted() { // 播报结束 reportSynthesizingProgress(new SynthesisProgress.Builder(SynthesisProgress.PROGRESS_ENDED).build()); resolveSynthesizing(); } }); } @Override protected void stopSynthesizing() { // 取消播报 googleSpeaker.stop(); } @Override public List<SpeakingVoice> getSpeakingVoiceList() { // 获取发音人信息 SpeakingVoice speaker = new SpeakingVoice.Builder("google") .setLanguage("en") .build(); return Collections.singletonList(speaker); } }
声明服务
实现了语音服务后,我们还需要做最后一步,声明已实现的服务。在项目的 Application 的 onCreate() 将已实现的服务声明
public class FooApplication extends Application { @Override public void onCreate() { //TODO Initialization service ServiceModules.initialize(this); //TODO Declare speech recognition service ServiceModules.declare( Synthesizer.class, new ModuleCreator<Synthesizer>() { @Override public void createModule(Class<Synthesizer> aClass, ModuleCreatedNotifier<Synthesizer> notifier) { notifier.notifyModuleCreated(new FooServices());//提供服务实例 } } ); } }
实现你的服务
-
语音识别服务(ASR)
public class YourRecognitionService extends AbstractRecognizer { @Override protected void startRecognizing(RecognitionOption recognitionOption) { /** recognitionOption 参数说明: * mode:识别模式,default:MODE_SINGLE * 1) MODE_SINGLE:单次识别; * 2) MODE_CONTINUOUS:连续识别; * 特别注意: * 若模式为 MODE_SINGLE, 识别结果应使用 resolveRecognizing 方法通知调用者识别结果, * 若模式为 MODE_CONTINUOUS, 识别结果应使用 reportRecognizingProgress 方法将结果通知给调用者. * distanceRange:识别范围,可选; * extension:扩展参数,可选; * understandingOption:语义理解参数,可选(部分引擎同时包含识别和语义理解); */ //TODO 你应该在此处添加识别的具体实现代码 //your function code... //TODO 识别进度处理 /** 1. 识别过程:如果你需要把识别过程通知给调用者,你可以调用该方法,并且你还可以在通知时携带一些信息. 注意: 该方法与 startRecognizing() 对应,可多次执行 持续识别模式下应使用此接口上报识别结果*/ reportRecognizingProgress( new RecognitionProgress.Builder(RecognitionProgress.PROGRESS_BEGAN) //进度类型: // 1) PROGRESS_BEGAN: 识别开始; // 2) PROGRESS_RECOGNIZING:识别中; // 3) PROGRESS_ENDED:识别结束; // 4) PROGRESS_RECOGNITION_TEXT_RESULT:识别结果; // 5) PROGRESS_UNDERSTANDING_RESULT:语义理解结果; .setTextResult("RecognitionResult") //识别结果,可选(MODE_CONTINUOUS 时,应填写识别结果在此处); .setDecibel(50) //识别到的声音大小,可选; .setUnderstandingResult(null) //语义理解结果,可选(部分引擎同时包含识别和语义理解); .build()); //TODO 识别结果处理 /** 2. 识别结束:当识别完成时,你可以调用该方法通知识别结果给调用者,. 注意: 该方法与 startRecognizing() 对应,只生效一次, 持续识别模式下如果使用此方法,本次识别将会结束*/ resolveRecognizing(new RecognitionResult .Builder("RecognitionResult") //识别结果(单次识别模式下必填) .setUnderstandingResult(null) //语义理解结果,可选(部分引擎同时包含识别和语义理解); .build()); //TODO 识别异常处理 /** 3. 识别异常:当识别发生错误时,你可以调用该方法通知调用者,该方法只生效一次. 注意: 该方法与 startRecognizing() 对应,只生效一次, 持续识别模式下如果使用此方法,本次识别将会结束*/ rejectRecognizing(new RecognitionException( RecognitionException.CODE_INTERNAL_ERROR, //错误码,使用给定的 123, //定义自己的错误码,可选 "Error Message") //错误信息,可选 ); } @Override protected void stopRecognizing() { //TODO 你应该在此处停止识别 //your function code... } }
-
语义理解服务(NLP)
public class YourUnderstandingService extends AbstractUnderstander { @Override protected void understand(UnderstandingOption understandingOption, Deferred<UnderstandingResult, UnderstandingException> deferred) { /** understandingOption 参数说明: * InputText:输入的文本; * SessionId:会话ID可选,可选; * ContextList:上下文列表,可选; * Parameters:参数集合,可选; * TimeoutMillis:超时时间,可选; * Version:版本,可选; */ //TODO 你应该在此处添加语义理解的具体实现代码 //your function code... //TODO 1. 语义理解完成处理,该方法与 understand() 对应,只生效一次 deferred.resolve(new UnderstandingResult .Builder() .setFulfillmentList(null) //语义理解返回的json内容,可选 .setSpeechFulfillment(null) //语义理解返回的数据,可选,TTS 默认播报该处内容 .setLanguage("en-rUS") //语言,可选 .setIntent(null) //语义理解返回的意图,分发skll时使用,可选 .setSessionId("") //SessionId,可选 (see UnderstandingOption.SessionId) .setSessionIncomplete(false) //多轮对话结束标志,可选 .setInputText("") //输入的文本,可选 (see UnderstandingOption.InputText) .setContextList(null) //上下文列表,可选 (see UnderstandingOption.ContextList) .setVersion("") //版本,可选 (see UnderstandingOption.Version) .setSource("") //平台,可选 .build()); //TODO 2. 语义理解异常处理,该方法与 understand() 对应,只生效一次 deferred.reject(new UnderstandingException( UnderstandingException.CODE_INTERNAL_ERROR, //错误码,使用给定的 123, //定义自己的错误码,可选 "Error Message") //错误信息,可选 ); } }
-
语音合成服务(TTS)
public class YourSynthesisService extends AbstractSynthesizer { @Override protected void startSynthesizing(SynthesisOption synthesisOption) { /** synthesisOption 参数说明: * inputText:合成内容; * speakingVoiceId:发音人; * speakingSpeed: 速度 0.1~1~10, default:1; * speakingVolume: 音量 0~100,default:0; */ //TODO 你应该在此处添加语音合成的具体实现代码 //your function code... //TODO 1. 合成过程通知,该方法与 startSynthesizing() 对应,可多次执行 reportSynthesizingProgress(new SynthesisProgress .Builder(SynthesisProgress.PROGRESS_BEGAN) //进度通知类型 // 1) PROGRESS_BEGAN 播报开始 // 2) PROGRESS_PLAYING 播报中 // 3) PROGRESS_ENDED 播报结束 .setAudioBytes(new byte[]{}) //音频数据,可选 .setPlayProgress(10) //进度百分比,可选 .setRemainingTimeMillis(10 * 1000) //剩余时间,可选 .build()); //TODO 2. 合成结束处理,该方法与 startSynthesizing() 对应,只生效一次 resolveSynthesizing(); //TODO 3. 合成异常处理,该方法与 startSynthesizing() 对应,只生效一次 rejectSynthesizing(new SynthesisException( SynthesisException.CODE_INTERNAL_ERROR, //错误码,使用给定的 123, //定义自己的错误码,可选 "Error Message")); //错误信息,可选 } @Override protected void stopSynthesizing() { //TODO 你应该在此处停止语音合成 //your function code... } @Override public List<SpeakingVoice> getSpeakingVoiceList() { //TODO 如果你拥有多个发音人,你应该在此给出支持的发音人列表 //your SpeakingVoiceList SpeakingVoice speakingVoice = new SpeakingVoice.Builder("1234") //发音人ID .setName("jack") //发音人名称 .setGender(0) //发音人性别 .setLanguage("en-rUS") //发音人对应语种 .build(); return Collections.singletonList(speakingVoice); } }
注意事项
- 首次安装时,设备需要重启,覆盖才能生效。
- 同一个系统中,请避免同时安装2个用于覆盖语音的 app
- 示例代码为针对google语音服务替换的代码:overriding-speech-service