001package com.aispeech.dui.dds.agent.tts;
002
003import android.text.TextUtils;
004
005import com.aispeech.aigson.AIGson;
006import com.aispeech.aigson.reflect.TypeToken;
007import com.aispeech.dui.BaseNode;
008import com.aispeech.dui.BusClient;
009import com.aispeech.dui.dds.DDS;
010import com.aispeech.dui.dds.agent.tts.bean.CustomAudioBean;
011import com.aispeech.dui.dds.exceptions.DDSNotInitCompleteException;
012import com.aispeech.dui.manager.AILog;
013import com.aispeech.libbase.bussiness.JSONObjectUtil;
014import com.aispeech.libcomm.business.call.TtsCallUtil;
015import com.aispeech.libcomm.business.config.AIConfig;
016import com.aispeech.libcomm.business.topic.PlayerTopicUtil;
017import com.aispeech.libcomm.business.topic.Topic;
018import com.aispeech.libcomm.business.topic.TtsTopicUtil;
019
020import org.json.JSONException;
021import org.json.JSONObject;
022
023import java.lang.reflect.Type;
024import java.util.ArrayList;
025import java.util.List;
026
027
028/**
029 * Created by nemo on 17-12-13.
030 */
031
032public class TTSEngine {
033
034    private static final String TAG = "TTSEngine";
035
036    public static final int LOCAL = 0;
037    public static final int CLOUD = 1;
038
039    public static final String TEXT = "text";
040    public static final String SSML = "ssml";
041
042    private volatile static TTSEngine mInstance;
043    private Callback mListener;
044    private String mBusServerAddr = "";
045    private AIGson mAIGson = new AIGson();
046    private BusClient mBc;
047    private String[] mTopics;
048
049    private final BaseNode mNode = new BaseNode() {
050
051        @Override
052        public String getName() {
053            return "TTSEngine";
054        }
055
056        @Override
057        public BusClient.RPCResult onCall(String url, Object... args) throws Exception {
058            return null;
059        }
060
061        @Override
062        public void onJoin() {
063            super.onJoin();
064            mBc = bc;
065            subscribe();
066        }
067
068        @Override
069        public void onMessage(String topic, Object... parts) {
070            if (Topic.Sys.TtsBegin.TOPIC_NAME.equals(topic)) {
071                JSONObject jsonObject = (JSONObject) parts[0];
072                String ttsId = jsonObject.optString(Topic.Sys.TtsBegin.TTS_ID);
073                String recordId = jsonObject.optString(Topic.Sys.TtsBegin.RECORD_ID);
074                if (TextUtils.isEmpty(ttsId)) {
075                    ttsId = "0";
076                }
077                synthesizeBegin(ttsId);
078                synthesizeBeginWithRecordId(ttsId,recordId);
079            } else if (Topic.Pcm.LocalTtsPcm.TOPIC_NAME.equals(topic) || Topic.Pcm.LocalTtsSpeakPcm.TOPIC_NAME.equals(topic)) {
080                received((byte[]) parts[0]);
081            } else if (Topic.Sys.TtsError.TOPIC_NAME.equals(topic)) {
082                String data = parts[0].toString();
083                error(data);
084            }  else if (Topic.Sys.TtsPhoneReturn.TOPIC_NAME.equals(topic)) {
085                String data = parts[0].toString();
086                phoneReturn(data);
087            }  else if (Topic.Sys.PlayerBegin.TOPIC_NAME.equals(topic)) {
088                JSONObject jsonObject = (JSONObject) parts[0];
089                String ttsId = jsonObject.optString(Topic.Sys.PlayerEnd.TTS_ID, "0");
090                playBegin(ttsId);
091            } else if (Topic.Sys.PlayerEnd.TOPIC_NAME.equals(topic)) {
092                JSONObject jsonObject = (JSONObject) parts[0];
093                String ttsId = jsonObject.optString(Topic.Sys.PlayerEnd.TTS_ID, "0");
094                int status = jsonObject.optInt(Topic.Sys.PlayerEnd.STATUS, 0);
095                playEnd(ttsId, status);
096            } else if (Topic.Sys.TtsEnd.TOPIC_NAME.equals(topic)) {
097                JSONObject jsonObject = (JSONObject) parts[0];
098                String ttsId = jsonObject.optString(Topic.Sys.TtsEnd.TTS_ID);
099                int status = jsonObject.optInt(Topic.Sys.TtsEnd.STATUS);
100                String recordId = jsonObject.optString(Topic.Sys.TtsEnd.RECORD_ID);
101                synthesizeEnd(ttsId, status);
102                synthesizeEndWithRecord(ttsId,status,recordId);
103            } else if (Topic.Sys.PlayerProcess.TOPIC_NAME.equals(topic)) {
104                if (mListener != null && mListener instanceof CallbackOptimize.InnerCallback) {
105                    JSONObject jsonObject = (JSONObject) parts[0];
106                    String ttsId = jsonObject.optString(Topic.Sys.PlayerProcess.TTS_ID);
107                    int currentFrame = jsonObject.optInt(Topic.Sys.PlayerProcess.CURRENT_FRAME);
108                    int totalFrame = jsonObject.optInt(Topic.Sys.PlayerProcess.TOTAL_FRAME);
109                    boolean isDataEnd = jsonObject.optBoolean(Topic.Sys.PlayerProcess.IS_DATA_END);
110                    ((CallbackOptimize.InnerCallback) mListener).onSpeechProgress(ttsId, currentFrame, totalFrame, isDataEnd);
111                }
112            }
113        }
114
115        @Override
116        public void onExit() {
117            if (mTopics != null && bc != null) {
118                bc.unsubscribe(mTopics);
119            }
120        }
121    };
122
123    private TTSEngine() {
124        mNode.start();
125    }
126
127    /**
128     * 获取TTSEngine
129     *
130     * @return TTSEngine实例
131     */
132    public static TTSEngine getInstance() {
133        TTSEngine localResource = mInstance;
134        if (localResource == null) {
135            synchronized (TTSEngine.class) {
136                localResource = mInstance;
137                if (localResource == null) {
138                    mInstance = localResource = new TTSEngine();
139                }
140            }
141        }
142        return localResource;
143    }
144
145    protected TTSEngine(String lBridgeAddr) {
146        mBusServerAddr = lBridgeAddr;
147        mNode.start();
148    }
149
150    /**
151     * 获取TTSEngine
152     *
153     * @param lBridgeAddr 指定busserver地址
154     * @return TTSEngine实例
155     */
156    public static TTSEngine getInstance(String lBridgeAddr) {
157        TTSEngine localResource = mInstance;
158        if (localResource == null) {
159            synchronized (TTSEngine.class) {
160                localResource = mInstance;
161                if (localResource == null) {
162                    mInstance = localResource = new TTSEngine(lBridgeAddr);
163                }
164            }
165        }
166        return localResource;
167    }
168
169    private void subscribe() {
170        if (mBc != null && mListener != null && mTopics != null) {
171            mBc.subscribe(mTopics);
172        } else {
173            if (mBc == null) {
174                AILog.e(TAG, "subscribe error -> bc is null");
175            }
176        }
177    }
178
179    /**
180     * 获取 TTSEngine 实例快照
181     *
182     * @return TTSEngine
183     */
184    public static TTSEngine getInstanceSnapshot() {
185        return mInstance;
186    }
187
188    /**
189     * 设置TTS相关事件的监听
190     *
191     * @param listener 回调各事件
192     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
193     */
194    public void setListener(Callback listener) throws DDSNotInitCompleteException {
195        AILog.userI(TAG, "setCallbackListener input =", (listener != null));
196        checkInitComplete();
197        mListener = listener;
198        mTopics = new String[]{
199                Topic.Sys.TtsBegin.TOPIC_NAME,
200                Topic.Sys.TtsEnd.TOPIC_NAME,
201                Topic.Pcm.LocalTtsPcm.TOPIC_NAME,
202                Topic.Pcm.LocalTtsSpeakPcm.TOPIC_NAME,
203                Topic.Sys.TtsError.TOPIC_NAME,
204                Topic.Sys.TtsPhoneReturn.TOPIC_NAME,
205                Topic.Sys.PlayerBegin.TOPIC_NAME,
206                Topic.Sys.PlayerEnd.TOPIC_NAME};
207
208        subscribe();
209    }
210
211    /**
212     * 设置TTS相关事件的监听, 精减音频的回调
213     *
214     * @param listener 回调各事件
215     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
216     */
217    public void setListener(CallbackOptimize listener) throws DDSNotInitCompleteException {
218        AILog.userI(TAG, "setCallbackOptimizeListener input =", (listener != null));
219        checkInitComplete();
220        mListener = listener.mInnerCallback;
221        mTopics = new String[]{
222                Topic.Sys.TtsBegin.TOPIC_NAME,
223                Topic.Sys.TtsEnd.TOPIC_NAME,
224                Topic.Sys.TtsError.TOPIC_NAME,
225                Topic.Sys.TtsPhoneReturn.TOPIC_NAME,
226                Topic.Sys.PlayerBegin.TOPIC_NAME,
227                Topic.Sys.PlayerEnd.TOPIC_NAME};
228        subscribe();
229    }
230
231    /**
232     * 设置TTS相关事件的监听, 精减音频的回调
233     *
234     * @param listener 回调各事件
235     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
236     */
237    public void setListenerByProcess(CallbackOptimize listener) throws DDSNotInitCompleteException {
238        checkInitComplete();
239        AILog.userI(TAG, "setListenerByProcess input =", (listener != null));
240        boolean enable = false;
241        if (listener == null) {
242            mListener = null;
243        } else {
244            mListener = listener.mInnerCallback;
245            enable = true;
246        }
247        BusClient bc = DDS.getInstance().getAgent().getBusClient();
248        if (bc != null) {
249            PlayerTopicUtil.publishSetPlayerEnableProcess(bc, enable);
250        } else {
251            AILog.e(TAG, "setListenerByProcress failed due to null busclient");
252        }
253        mTopics = new String[]{
254                Topic.Sys.TtsBegin.TOPIC_NAME,
255                Topic.Sys.TtsEnd.TOPIC_NAME,
256                Topic.Sys.TtsError.TOPIC_NAME,
257                Topic.Sys.PlayerBegin.TOPIC_NAME,
258                Topic.Sys.PlayerEnd.TOPIC_NAME,
259                Topic.Sys.TtsPhoneReturn.TOPIC_NAME,
260                Topic.Sys.PlayerProcess.TOPIC_NAME};
261        subscribe();
262    }
263
264    private void synthesizeBegin(String ttsId) {
265        if (mListener != null) {
266            mListener.synthesizeBegin(ttsId);
267        }
268    }
269
270    private void synthesizeBeginWithRecordId(String ttsId,String recordId) {
271        if (mListener != null) {
272            mListener.synthesizeBeginWithRecord(ttsId,recordId);
273        }
274    }
275
276    private void received(byte[] data) {
277        if (mListener != null) {
278            mListener.received(data);
279        }
280    }
281
282    private void phoneReturn(String phoneReturn) {
283        if (mListener != null) {
284            mListener.phoneReturnReceived(phoneReturn);
285        }
286    }
287
288    private void synthesizeEnd(String ttsId,int status) {
289        if (mListener != null) {
290            mListener.synthesizeEnd(ttsId,status);
291        }
292    }
293
294    private void playBegin(String ttsId) {
295        if (mListener != null) {
296            mListener.playBegin(ttsId);
297        }
298    }
299
300    private void playEnd(String ttsId, int status) {
301        if (mListener != null) {
302            mListener.playEnd(ttsId, status);
303        }
304    }
305
306    private void synthesizeEndWithRecord(String ttsId, int status,String recordId) {
307        if (mListener != null) {
308            mListener.synthesizeEndWithRecordId(ttsId, status,recordId);
309        }
310    }
311
312    private void error(String error) {
313        if (mListener != null) {
314            mListener.error(error);
315        }
316    }
317
318    /**
319     * 播报文本,支持SSML
320     * <p>
321     * 当DDSConfig.K_TTS_MODE设置为"external"时,该接口无效,请直接调用外部TTS引擎的对应接口。
322     *
323     * @param text     播报文本
324     * @param priority 优先级
325     *                 <ul>
326     *                 <li>优先级0-保留,与aios语音交互同级,仅限内部使用</li>
327     *                 <li>优先级1-正常,默认选项,同级按序播放</li>
328     *                 <li>优先级2-重要,可以插话优先级1,同级按序播放,播报完毕后继续播报刚才被插话的优先级1</li>
329     *                 <li>优先级3-紧急,可以打断优先级1或优先级2,同级按序播放,播报完毕后播报下一句优先级2</li>
330     *                 </ul>
331     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
332     */
333    public void speak(String text, int priority) throws DDSNotInitCompleteException {
334        speak(text, priority, "0", -1, TTSEngine.TEXT);
335    }
336
337    /**
338     * 播报文本,支持SSML
339     * <p>
340     * 当DDSConfig.K_TTS_MODE设置为"external"时,该接口无效,请直接调用外部TTS引擎的对应接口。
341     *
342     * @param text     播报文本
343     * @param priority 优先级
344     * @param type     文本的类型,{@link TTSEngine#TEXT} or {@link TTSEngine#SSML}
345     *                 <ul>
346     *                 <li>优先级0-保留,与aios语音交互同级,仅限内部使用</li>
347     *                 <li>优先级1-正常,默认选项,同级按序播放</li>
348     *                 <li>优先级2-重要,可以插话优先级1,同级按序播放,播报完毕后继续播报刚才被插话的优先级1</li>
349     *                 <li>优先级3-紧急,可以打断优先级1或优先级2,同级按序播放,播报完毕后播报下一句优先级2</li>
350     *                 </ul>
351     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
352     */
353    public void speak(String text, int priority, String type) throws DDSNotInitCompleteException {
354        speak(text, priority, "0", -1, type);
355    }
356
357    /**
358     * 播报文本,支持SSML
359     * <p>
360     * 当DDSConfig.K_TTS_MODE设置为"external"时,该接口无效,请直接调用外部TTS引擎的对应接口。
361     *
362     * @param text       播报文本
363     * @param priority   优先级
364     *                   <ul>
365     *                   <li>优先级0-保留,与aios语音交互同级,仅限内部使用;</li>
366     *                   <li>优先级1-正常,默认选项,同级按序播放;</li>
367     *                   <li>优先级2-重要,可以插话优先级1,同级按序播放,播报完毕后继续播报刚才被插话的优先级1</li>
368     *                   <li>优先级3-紧急,可以打断优先级1或优先级2,同级按序播放,播报完毕后播报下一句优先级2</li>
369     *                   </ul>
370     * @param ttsId      用于追踪该次播报的id,建议使用UUID.
371     * @param audioFocus 该次播报的音频焦
372     *                   <ul>
373     *                   <li>priority == 0
374     *                   {@link android.media.AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}</li>
375     *                   <li>priority != 0
376     *                   {@link android.media.AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}</li>
377     *                   </ul>
378     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
379     */
380    public void speak(String text, int priority, String ttsId, int audioFocus) throws DDSNotInitCompleteException {
381        speak(text, priority, ttsId, audioFocus, TTSEngine.TEXT);
382    }
383
384    /**
385     * 播报文本,支持SSML
386     * <p>
387     * 当DDSConfig.K_TTS_MODE设置为"external"时,该接口无效,请直接调用外部TTS引擎的对应接口。
388     *
389     * @param text       播报文本
390     * @param priority   优先级
391     *                   <ul>
392     *                   <li>优先级0-保留,与aios语音交互同级,仅限内部使用;</li>
393     *                   <li>优先级1-正常,默认选项,同级按序播放;</li>
394     *                   <li>优先级2-重要,可以插话优先级1,同级按序播放,播报完毕后继续播报刚才被插话的优先级1</li>
395     *                   <li>优先级3-紧急,可以打断优先级1或优先级2,同级按序播放,播报完毕后播报下一句优先级2</li>
396     *                   </ul>
397     * @param ttsId      用于追踪该次播报的id,建议使用UUID.
398     * @param audioFocus 该次播报的音频焦
399     * @param type       文本的类型,{@link TTSEngine#TEXT} or {@link TTSEngine#SSML}
400     *                   <ul>
401     *                   <li>priority == 0
402     *                   {@link android.media.AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE}</li>
403     *                   <li>priority != 0
404     *                   {@link android.media.AudioManager#AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK}</li>
405     *                   </ul>
406     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
407     */
408    public void speak(String text, int priority, String ttsId, int audioFocus, String type) throws DDSNotInitCompleteException {
409        checkInitComplete();
410        AILog.userI(TAG, "speak input =", text, ", priority =", priority, ", ttsId =", ttsId, ", audioFocus =", audioFocus, ", type =", type);
411        BusClient bc = DDS.getInstance().getAgent().getBusClient();
412        if (bc != null) {
413            TtsTopicUtil.publishSpeak(bc, text, priority, ttsId, audioFocus, type);
414        } else {
415            AILog.e(TAG, "speak failed due to null busclient");
416        }
417    }
418
419    /**
420     * 播放器静音
421     *
422     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
423     */
424    public void mutePlayer() throws DDSNotInitCompleteException {
425        checkInitComplete();
426        AILog.userI(TAG, "mutePlayer");
427        BusClient bc = DDS.getInstance().getAgent().getBusClient();
428        if (bc != null) {
429            PlayerTopicUtil.publishSetPlayerVolume(bc, "mute");
430        } else {
431            AILog.e(TAG, "mutePlayer failed due to null busclient");
432        }
433    }
434
435    /**
436     * 播放器恢复声音播放
437     *
438     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
439     */
440    public void unmutePlayer() throws DDSNotInitCompleteException {
441        checkInitComplete();
442        AILog.userI(TAG, "unmutePlayer");
443        BusClient bc = DDS.getInstance().getAgent().getBusClient();
444        if (bc != null) {
445            PlayerTopicUtil.publishSetPlayerVolume(bc, "unmute");
446        } else {
447            AILog.e(TAG, "unmutePlayer failed due to null busclient");
448        }
449    }
450
451    /**
452     * 停止播报接口
453     * <p>
454     * 当DDSConfig.K_TTS_MODE设置为"external"时,该接口无效,请直接调用外部TTS引擎的对应接口。
455     *
456     * @param ttsId 和 {@link #speak(String, int, String, int)} ttsId.一致
457     *              ttsId与speak接口的ttsId一致,则停止或者移除该播报;
458     *              ttsId为空, 停止所有播报;
459     *              ttsId为"0",停止当前播报.
460     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
461     */
462    public void shutup(String ttsId) throws DDSNotInitCompleteException {
463        checkInitComplete();
464        AILog.userI(TAG, "shutup input =", ttsId);
465        BusClient bc = DDS.getInstance().getAgent().getBusClient();
466        if (bc != null) {
467            TtsTopicUtil.publishShutup(bc, ttsId == null ? "" : ttsId);
468        } else {
469            AILog.e(TAG, "shutup failed due to null busclient");
470        }
471    }
472
473    /**
474     * 设置TTS播报类型的接口
475     * <p>
476     * <p>
477     * 调用此接口则云端配置的合成音类型失效,此后的合成音类型都将由此接口来托管
478     *
479     * @param speaker 取值如:zhilingf, gdgm等,若取为null,则使用产品配置的音色
480     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
481     */
482    public void setSpeaker(String speaker) throws DDSNotInitCompleteException {
483        setSpeaker(speaker, "");
484    }
485
486    /**
487     * 设置TTS播报类型的接口
488     * <p>
489     * <p>
490     * 调用此接口则云端配置的合成音类型失效,此后的合成音类型都将由此接口来托管
491     *
492     * @param requestBean tts的请求参数,包括音色,采样率等信息
493     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
494     */
495    public void setSpeaker(TtsSpeakerRequestBean requestBean) throws DDSNotInitCompleteException {
496        checkInitComplete();
497        BusClient bc = DDS.getInstance().getAgent().getBusClient();
498        if (bc != null) {
499            String ttsSpeakerStr = "";
500            if (requestBean != null) {
501                JSONObject ttsSpeakerObj = requestBean.getTtsSpeakObj();
502                ttsSpeakerStr = ttsSpeakerObj.toString();
503                AILog.userI(TAG, "setSpeaker input =", ttsSpeakerStr);
504                TtsTopicUtil.publishSetSpeaker(bc, requestBean.getTtsSpeakObj());
505            }
506        } else {
507            AILog.e(TAG, "setSpeaker failed due to null busclient");
508        }
509    }
510
511    /**
512     * 设置TTS播报类型的接口
513     * <p>
514     * <p>
515     * 调用此接口则云端配置的合成音类型失效,此后的合成音类型都将由此接口来托管
516     * @param speaker 取值如:zhilingf, gdgm等,若取为null,则使用产品配置的音色
517     * @param resPath 合成资源的全路径: sdcard/aispeech/zhilingf.bin
518     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
519     */
520    public void setSpeaker(String speaker, String resPath) throws DDSNotInitCompleteException {
521        setSpeaker(speaker, resPath, "");
522    }
523
524    /**
525     * 设置TTS播报采样率,适配云端24K,默认云端不返回采样率
526     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
527     */
528    public void setSpeakerSampleRate(int sampleSize) throws DDSNotInitCompleteException {
529        setSpeakerSampleRate(sampleSize,false);
530    }
531
532    /**
533     * 设置TTS播报采样率,适配云端24K,默认云端不返回采样率
534     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
535     */
536    public void setSpeakerSampleRate(int sampleSize,boolean syncCloud) throws DDSNotInitCompleteException {
537        checkInitComplete();
538        BusClient bc = DDS.getInstance().getAgent().getBusClient();
539        if (bc != null) {
540            TtsTopicUtil.publishSetSpeakerSampleRate(bc, sampleSize,syncCloud);
541            TtsTopicUtil.publishSetSpeakerSampleRateForPlayer(mBc, sampleSize);
542        } else {
543            AILog.e(TAG, "setSpeaker failed due to null busclient");
544        }
545    }
546
547    /**
548     * 设置TTS是否返回音素信息,因为历史遗留音素,会存在下列问题
549     * 部分音色会忽略音素设置,设置为true依旧不会返回信息,并抛出错误码72205,
550     *          如anonyg hbrinf hyanif lunaif_ctn lzyinf swkm zxcm zzxiangm等
551     * 部分音素支持返回音素,
552     *          如cyangfp dyb gdfanfp gqlanfp hthy jjingfp jlshim lanyuf lchuam lili1f_yubo lucyfa
553     *          lzliafp madoufp_wenrou madoufp_yubo xbekef xijunma xjingfp xyb xynmamp ychanmp yhchu
554     *          zhilingfp zhilingfp_huankuai zsmeif dksjif ybyuaf sqksaf zxiyum aningfp lmyanm
555     *          wqingf_csn ppangf_csn hchunf_ctn mamif xmguof等
556     * 其余的音色不支持音素,不会返回音素信息,但是也不会抛出错误码
557     * @param phoneReturn 音素开关;
558     */
559    public void setPhoneReturn(boolean phoneReturn) throws DDSNotInitCompleteException {
560        checkInitComplete();
561        BusClient bc = DDS.getInstance().getAgent().getBusClient();
562        if (bc != null) {
563            TtsTopicUtil.publishSetPhoneReturn(bc, phoneReturn);
564        } else {
565            AILog.e(TAG, "setPhoneReturn failed due to null busclient");
566        }
567
568    }
569
570    private void setSpeaker(String speaker, String resPath, String userId) throws DDSNotInitCompleteException {
571        checkInitComplete();
572        AILog.userI(TAG, "setSpeaker input =", speaker, ", resPath =", resPath, ", userId =", userId);
573        BusClient bc = DDS.getInstance().getAgent().getBusClient();
574        if (bc != null) {
575            TtsTopicUtil.publishSetSpeaker(bc, speaker, resPath, userId);
576        } else {
577            AILog.e(TAG, "setSpeaker failed due to null busclient");
578        }
579    }
580
581    /**
582     * 动态设置人声复刻的server地址和userId
583     *
584     * @param speaker String 人声复刻voiceId
585     * @param userId  String 人声复刻voiceId对应的userId
586     */
587    public void setSpeakerAndUserId(String speaker, String userId) throws DDSNotInitCompleteException {
588        setSpeaker(speaker, "", userId);
589    }
590
591    /**
592     * 设置TTS人设
593     * 相对的api {@link TTSEngine#removeStyle()}
594     *
595     * @param style 风格,humor:幽默;calm:沉稳;common:普通;简短:short;
596     */
597    public void setStyle(String style) throws DDSNotInitCompleteException {
598        checkInitComplete();
599        AILog.userI(TAG, "setStyle input =", style);
600        if (TextUtils.isEmpty(style)) {
601            AILog.w(TAG, "setStyle style is Empty!!!");
602            return;
603        }
604        BusClient bc = DDS.getInstance().getAgent().getBusClient();
605        if (bc != null) {
606            TtsTopicUtil.publishSetStyle(bc, style, "update");
607        } else {
608            AILog.e(TAG, "setStyle failed due to null busclient");
609        }
610
611    }
612
613    /**
614     * 清除TTS人设
615     * 相对的api {@link TTSEngine#setStyle(String)}
616     */
617    public void removeStyle() throws DDSNotInitCompleteException {
618        checkInitComplete();
619        AILog.userI(TAG, "removeStyle");
620        BusClient bc = DDS.getInstance().getAgent().getBusClient();
621        if (bc != null) {
622            TtsTopicUtil.publishSetStyle(bc, "", "remove");
623        } else {
624            AILog.e(TAG, "removeStyle failed due to null busclient");
625        }
626    }
627
628    /**
629     * 设置自定义TTS播报录音的接口
630     * <p>
631     * <p>
632     * 调用此接口则表态配置的播报录音失效,以动态设置的为准
633     *
634     * @param customAudioList 取值如:自定义播报音频列表
635     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
636     */
637    public void setCustomAudio(List<CustomAudioBean> customAudioList) throws DDSNotInitCompleteException {
638        checkInitComplete();
639        String customAudioJson = "[]";
640        if (customAudioList != null) {
641            customAudioJson = mAIGson.toJson(customAudioList);
642        }
643        AILog.userI(TAG, "setCustomAudio input =", customAudioJson);
644        BusClient bc = DDS.getInstance().getAgent().getBusClient();
645        if (bc != null) {
646            TtsTopicUtil.publishSetCustomAudio(bc, customAudioJson);
647        } else {
648            AILog.e(TAG, "setCustomAudio failed due to null busclient");
649        }
650    }
651
652    /**
653     * 获取自定义TTS播报录音的接口
654     * <p>
655     * <p>
656     *
657     * @return 自定义TTS播报录音
658     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
659     */
660    public List<CustomAudioBean> getCustomAudio() throws DDSNotInitCompleteException {
661        checkInitComplete();
662        BusClient bc = DDS.getInstance().getAgent().getBusClient();
663        if (bc != null) {
664            String customAudios = TtsCallUtil.callGetCustomAudio(bc);
665            if (!TextUtils.isEmpty(customAudios)) {
666                Type listType = new TypeToken<ArrayList<CustomAudioBean>>() {
667                }.getType();
668                AILog.userI(TAG, "getCustomAudio result =", customAudios);
669                return mAIGson.fromJson(customAudios, listType);
670            }
671        } else {
672            AILog.e(TAG, "getCustomAudio failed due to null busclient");
673        }
674        AILog.userI(TAG, "getCustomAudio result = null");
675        return null;
676    }
677
678    /**
679     * 设置TTS播报音量的接口
680     * <p>
681     * <p>
682     * 调用此接口则云端配置的合成音音量失效,此后的合成音音量都将由此接口来托管
683     *
684     * @param volume 音量大小,取值1-500
685     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
686     */
687    public void setVolume(int volume) throws DDSNotInitCompleteException {
688        checkInitComplete();
689        AILog.userI(TAG, "setVolume input =", volume);
690        BusClient bc = DDS.getInstance().getAgent().getBusClient();
691        if (bc != null) {
692            TtsTopicUtil.publishSetVolume(bc, "absolute", volume);
693        } else {
694            AILog.e(TAG, "setVolume failed due to null busclient");
695        }
696    }
697
698
699    /**
700     * 获取dui平台的tts配置
701     *
702     * @return {"speaker":"zhilingf","volume":50,"type":"mode","speed":0.85}
703     */
704    public String getTtsAIConf() {
705        String aiconf = AIConfig.getInstance().getCustomConfig("tts");
706        try {
707            JSONObject aiconfJo = new JSONObject(aiconf);
708            String voice = aiconfJo.optString("voice");
709            String mode = aiconfJo.optString("type");
710            int volume = aiconfJo.optInt("volume");
711            double speed = aiconfJo.optDouble("speed");
712            JSONObject retJo = new JSONObject();
713            retJo.put("speaker", voice);
714            retJo.put("mode", mode);
715            retJo.put("volume", volume);
716            retJo.put("speed", speed);
717            AILog.userI(TAG, "getTtsAIConf result =", retJo.toString());
718            return retJo.toString();
719        } catch (JSONException e) {
720            com.aispeech.dui.manager.AIJavaException.printException(e);
721        }
722        AILog.userI(TAG, "getTtsAIConf result =", aiconf);
723
724        return aiconf;
725    }
726
727
728    /**
729     * 设置TTS播报语速的接口
730     * <p>
731     * <p>
732     * 调用此接口则云端配置的合成音语速失效,此后的合成音语速都将由此接口来托管
733     *
734     * @param speed 语速,取值0.5-2.0,0.5语速最快,2.0语速最慢
735     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
736     */
737    public void setSpeed(float speed) throws DDSNotInitCompleteException {
738        checkInitComplete();
739        AILog.userI(TAG, "setSpeed input =", speed);
740        BusClient bc = DDS.getInstance().getAgent().getBusClient();
741        if (bc != null) {
742            TtsTopicUtil.publishSetSpeed(bc, "absolute", speed);
743        } else {
744            AILog.e(TAG, "setSpeed failed due to null busclient");
745        }
746    }
747
748    /**
749     * 获取当前TTS人设
750     *
751     * @return String 当前 TTS 人设的类型
752     */
753    public String getStyle() throws DDSNotInitCompleteException {
754        checkInitComplete();
755        BusClient bc = DDS.getInstance().getAgent().getBusClient();
756        String style = null;
757        if (bc != null) {
758            style = TtsCallUtil.callGetStyle(bc);
759        } else {
760            AILog.e(TAG, "getStyle failed due to null busclient");
761        }
762        AILog.userI(TAG, "getStyle result =", style);
763        return style;
764    }
765
766
767    /**
768     * 获取当前使用的合成音类型
769     * <p>
770     *
771     * @return String 当前使用的合成音类型,如:"zhilingf",获取失败返回null
772     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
773     */
774    public String getSpeaker() throws DDSNotInitCompleteException {
775        checkInitComplete();
776        BusClient bc = DDS.getInstance().getAgent().getBusClient();
777        String speaker = null;
778        if (bc != null) {
779            speaker = TtsCallUtil.callGetspeaker(bc);
780        } else {
781            AILog.e(TAG, "getCurrentSpeaker failed due to null busclient");
782        }
783        AILog.userI(TAG, "getSpeaker result =", speaker);
784        return speaker;
785    }
786
787    /**
788     * 获取当前使用的合成音语速
789     * <p>
790     *
791     * @return float 当前合成音语速,返回值0.5-2.0,0.5语速最快,2.0语速最慢,获取失败返回 0
792     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
793     */
794    public float getSpeed() throws DDSNotInitCompleteException {
795        checkInitComplete();
796        BusClient bc = DDS.getInstance().getAgent().getBusClient();
797        float speed = 0;
798        if (bc != null) {
799            speed = TtsCallUtil.callGetspeed(bc);
800        } else {
801            AILog.e(TAG, "getCurrentSpeed failed due to null busclient");
802        }
803        AILog.userI(TAG, "getSpeed result =", speed);
804        return speed;
805    }
806
807    /**
808     * 获取当前使用的合成音音量
809     * <p>
810     *
811     * @return int 当前合成音音量,返回值1-100,获取失败返回 0
812     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
813     */
814    public int getVolume() throws DDSNotInitCompleteException {
815        checkInitComplete();
816        BusClient bc = DDS.getInstance().getAgent().getBusClient();
817        int volume = 0;
818        if (bc != null) {
819            volume = TtsCallUtil.callGetvolume(bc);
820        } else {
821            AILog.e(TAG, "getCurrentVolume failed due to null busclient");
822        }
823        AILog.userI(TAG, "getVolume result =", volume);
824        return volume;
825    }
826
827    /**
828     * 设置合成音为本地合成或者云端合成
829     *
830     * @param mode 取值 TTSEngine.LOCAL(本地合成)
831     *             TTSEngine.CLOUD(云端合成)
832     * @throws DDSNotInitCompleteException 如果DDS没有初始化完成,会抛出exception
833     */
834    public void setMode(int mode) throws DDSNotInitCompleteException {
835        checkInitComplete();
836        AILog.userI(TAG, "setMode input =", mode);
837        BusClient bc = DDS.getInstance().getAgent().getBusClient();
838        if (mode != LOCAL && mode != CLOUD) {
839            AILog.e(TAG, "setMode param is no support, ignore");
840            return;
841        }
842        if (bc != null) {
843            TtsTopicUtil.publishSetMode(bc, mode == LOCAL ? "local" : "cloud");
844        } else {
845            AILog.e(TAG, "setMode failed due to null busclient");
846        }
847    }
848
849    /**
850     * 设备抢焦点,默认为抢焦点
851     *
852     * @param enable true/false  true: 抢焦点  false: 不抢焦点
853     */
854    public void enableFocus(boolean enable) throws DDSNotInitCompleteException {
855        checkInitComplete();
856        AILog.userI(TAG, "enableFocus input =", enable);
857        BusClient bc = DDS.getInstance().getAgent().getBusClient();
858        if (bc != null) {
859            PlayerTopicUtil.publishSetPlayerEnableFocus(bc, enable);
860        } else {
861            AILog.e(TAG, "enableFocus failed due to null busclient");
862        }
863    }
864
865    /**
866     * 动态设置 云端tts 合成地址
867     *
868     * @param tts_server String
869     */
870    public void setTtsServer(String tts_server) throws DDSNotInitCompleteException {
871        checkInitComplete();
872        AILog.userI(TAG, "setTtsServer input =", tts_server);
873        BusClient bc = DDS.getInstance().getAgent().getBusClient();
874        if (bc != null) {
875            TtsTopicUtil.publishSetServer(bc, tts_server);
876        } else {
877            AILog.e(TAG, "setTtsServer failed due to null busclient");
878        }
879    }
880
881    /**
882     * 设置TTS结束后延迟时间,单位ms
883     *
884     * @param afterTime
885     * @throws DDSNotInitCompleteException
886     */
887    public void setPlayAfterTime(int afterTime) throws DDSNotInitCompleteException {
888        checkInitComplete();
889        AILog.userI(TAG, "setPlayAfterTime input =", afterTime);
890        BusClient bc = DDS.getInstance().getAgent().getBusClient();
891        if (bc != null) {
892            PlayerTopicUtil.publishSetPlayerAfterTime(bc, afterTime);
893        } else {
894            AILog.e(TAG, "setPlayAfterTime failed due to null busclient");
895        }
896    }
897
898    /**
899     * 设置TTS播报的通道
900     *
901     * @param streamType
902     * @throws DDSNotInitCompleteException
903     */
904    public void setStreamType(int streamType) throws DDSNotInitCompleteException {
905        checkInitComplete();
906        AILog.userI(TAG, "setStreamType input =", streamType);
907
908        BusClient bc = DDS.getInstance().getAgent().getBusClient();
909        if (bc != null) {
910            TtsTopicUtil.publishSetStream(bc, streamType);
911        } else {
912            AILog.e(TAG, "setStreamType failed due to null busclient");
913        }
914    }
915
916    /**
917     * 设置TTS播报的通道
918     *
919     * @param usage       取值: AudioAttributes.USAGE_*
920     * @param contentType 取值: AudioAttributes.CONTENT_TYPE_*
921     * @throws DDSNotInitCompleteException
922     */
923    public void setUsage(int usage, int contentType) throws DDSNotInitCompleteException {
924        checkInitComplete();
925        AILog.userI(TAG, "setUsage usage =", usage, ", contentType =", contentType);
926
927        BusClient bc = DDS.getInstance().getAgent().getBusClient();
928        if (bc != null) {
929            TtsTopicUtil.publishSetUsage(bc, usage, contentType);
930        } else {
931            AILog.e(TAG, "setContentType failed due to null busclient");
932        }
933    }
934
935    public void setGainType(int gainType) throws DDSNotInitCompleteException {
936        checkInitComplete();
937        AILog.userI(TAG, "setGainType input =", gainType);
938
939        BusClient bc = DDS.getInstance().getAgent().getBusClient();
940        if (bc != null) {
941            TtsTopicUtil.publishSetGainType(bc, gainType);
942        } else {
943            AILog.e(TAG, "setGainType failed due to null busclient");
944        }
945    }
946
947    private void checkInitComplete() throws DDSNotInitCompleteException {
948        if (DDS.getInstance().getInitStatus() != DDS.INIT_COMPLETE_FULL) {
949            throw new DDSNotInitCompleteException();
950        }
951    }
952
953    public interface Callback {
954        /**
955         * 开始合成时的回调
956         *
957         * @param ttsId 当前TTS的id, 对话过程中的播报ttsid默认为0,通过speak接口调用的播报,ttsid由speak接口指定。
958         */
959        void synthesizeBegin(String ttsId);
960
961        void synthesizeBeginWithRecord(String ttsId,String recordId);
962
963        /**
964         * 合成的音频数据的回调,可能会返回多次,data长度为0表示音频结束
965         *
966         * @param data 音频数据
967         */
968        void received(byte[] data);
969
970        /**
971         * TTS播报完成的回调
972         *
973         * @param ttsId  当前TTS的id, 对话过程中的播报ttsid默认为0,通过speak接口调用的播报,ttsid由speak接口指定。
974         * @param status 播报结束的状态。
975         *               正常播报结束为0
976         *               播报中途被打断结束为1
977         */
978        void synthesizeEnd(String ttsId, int status);
979
980        void synthesizeEndWithRecordId(String ttsId,int status,String recordId);
981
982        void playBegin(String ttsId);
983
984        void playEnd(String ttsId, int status);
985
986        /**
987         * 合成过程中出现错误的回调s
988         *
989         * @param error 错误信息
990         */
991        void error(String error);
992
993        /**
994         * 合成的音素数据的回调,可能会返回多次
995         *
996         * @param phoneReturn 返回的音素信息
997         */
998        void phoneReturnReceived(String phoneReturn);
999    }
1000
1001    public static abstract class CallbackOptimize {
1002        private InnerCallback mInnerCallback;
1003
1004        public CallbackOptimize() {
1005            mInnerCallback = new InnerCallback(this);
1006        }
1007
1008        public void received(byte[] data) {
1009
1010        }
1011
1012        /**
1013         * 开始合成时的回调
1014         *
1015         * @param ttsId 当前TTS的id, 对话过程中的播报ttsid默认为0,通过speak接口调用的播报,ttsid由speak接口指定。
1016         */
1017        public void synthesizeBegin(String ttsId) {
1018
1019        }
1020
1021        public void synthesizeBeginWidthRecordId(String ttsId,String recordId) {
1022
1023        }
1024
1025        /**
1026         * TTS播报完成的回调
1027         *
1028         * @param ttsId  当前TTS的id, 对话过程中的播报ttsid默认为0,通过speak接口调用的播报,ttsid由speak接口指定。
1029         * @param status 播报结束的状态。
1030         *               正常播报结束为0
1031         *               播报中途被打断结束为1
1032         */
1033        public void synthesizeEnd(String ttsId, int status) {
1034
1035        }
1036
1037        public void synthesizeEndWithRecordId(String ttsId, int status,String recordId) {
1038
1039        }
1040
1041        public void playBegin(String ttsId) {
1042
1043        }
1044
1045        public void playEnd(String ttsId, int status) {
1046
1047        }
1048
1049
1050        /**
1051         * TTS播报进度的回调
1052         *
1053         * @param ttsId        当前TTS的id, 对话过程中的播报ttsid默认为0,通过speak接口调用的播报,ttsid由speak接口指定。
1054         * @param currentFrame 当前播放器已经播放的帧数
1055         * @param totalFrame   当前tts总下载帧数
1056         * @param isDataReady  数据是否下载完成
1057         */
1058        public void onSpeechProgress(String ttsId, int currentFrame, int totalFrame, boolean isDataReady) {
1059        }
1060
1061        /**
1062         * 合成过程中出现错误的回调
1063         *
1064         * @param error 错误信息
1065         */
1066        public void error(String error) {
1067
1068        }
1069
1070        /**
1071         * 合成的音素数据的回调,可能会返回多次
1072         *
1073         * @param phoneReturn 返回的音素信息
1074         */
1075        public void phoneReturnReceived(String phoneReturn) {
1076
1077        }
1078
1079        private static class InnerCallback implements Callback {
1080            CallbackOptimize optimize;
1081
1082            public InnerCallback(CallbackOptimize optimize) {
1083                this.optimize = optimize;
1084            }
1085
1086            @Override
1087            public void synthesizeBegin(String ttsId) {
1088                optimize.synthesizeBegin(ttsId);
1089            }
1090
1091            @Override
1092            public void synthesizeBeginWithRecord(String ttsId, String recordId) {
1093                optimize.synthesizeBeginWidthRecordId(ttsId,recordId);
1094            }
1095
1096            @Override
1097            public void received(byte[] data) {
1098                optimize.received(data);
1099            }
1100
1101            @Override
1102            public void synthesizeEnd(String ttsId, int status) {
1103                optimize.synthesizeEnd(ttsId,status);
1104            }
1105
1106            @Override
1107            public void synthesizeEndWithRecordId(String ttsId, int status, String recordId) {
1108                optimize.synthesizeEndWithRecordId(ttsId,status,recordId);
1109            }
1110
1111            @Override
1112            public void playBegin(String ttsId) {
1113                optimize.playBegin(ttsId);
1114            }
1115
1116            @Override
1117            public void playEnd(String ttsId, int status) {
1118                optimize.playEnd(ttsId,status);
1119            }
1120
1121            public void onSpeechProgress(String ttsId, int currentFrame, int totalFrame, boolean isDataReady) {
1122                optimize.onSpeechProgress(ttsId, currentFrame, totalFrame, isDataReady);
1123            }
1124
1125            @Override
1126            public void error(String error) {
1127                optimize.error(error);
1128            }
1129
1130            @Override
1131            public void phoneReturnReceived(String phoneReturn) {
1132                optimize.phoneReturnReceived(phoneReturn);
1133            }
1134        }
1135    }
1136
1137    public interface SpeechProcessorListener {
1138        void onSpeechProgress(String ttsId, int currentFrame, int totalFrame, boolean isDataReady);
1139    }
1140
1141    public void destroy() {
1142        if (mInstance != null) {
1143            if (mInstance.mNode != null) {
1144                mInstance.mNode.stop();
1145            }
1146            mInstance = null;
1147        }
1148    }
1149
1150    // 识别声纹请求业务bean
1151    public static class TtsSpeakerRequestBean {
1152        private String speaker = "";
1153        private String resPath = "";
1154        private String userId = "";
1155        private int sampleRate = 16000;
1156        private int localSampleRate = 16000;
1157
1158        public TtsSpeakerRequestBean setSpeaker(String speaker) {
1159            this.speaker = speaker;
1160            return this;
1161        }
1162
1163        public TtsSpeakerRequestBean setResPath(String resPath) {
1164            this.resPath = resPath;
1165            return this;
1166        }
1167
1168        public TtsSpeakerRequestBean setUserId(String userId) {
1169            this.userId = userId;
1170            return this;
1171        }
1172
1173        public TtsSpeakerRequestBean setSampleRate(int sampleRate) {
1174            this.sampleRate = sampleRate;
1175            return this;
1176        }
1177
1178        public TtsSpeakerRequestBean setLocalSampleRate(int localSampleRate) {
1179            this.localSampleRate = localSampleRate;
1180            return this;
1181        }
1182
1183        public JSONObject getTtsSpeakObj() {
1184            return JSONObjectUtil.create()
1185                    .put("value", speaker)
1186                    .put("resPath", resPath)
1187                    .put("userId", userId)
1188                    .put("sampleRate", sampleRate)
1189                    .put("localSampleRate", localSampleRate)
1190                    .build();
1191        }
1192    }
1193}