Android科大讯飞语音按队列播报

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/wb_001/article/details/78561227

在Android开发中,语音播报经常使用,但是,在使用过程中会发现,如果语音来源过于块,频率过高,在前一条没有播放完的情况下会执行第二条播放,本篇文章用来解决这个问题。

一、科大讯飞语音集成:
1、在科大讯飞的开发者平台(科大讯飞开放平台)注册。我这里使用的是在线语音合成,下载SDK包。
2、将SDK包里面的libs里面的.so文件和jar包复制到你的项目中,并且引用.so文件和jar包,这里要注意的是,个人建议将所有CPU类型的.so文件都放到你的项目中,这样保证在所有设备中都能播放,虽然这样会导致项目变大。

3、在MyApplication中初始化:
SpeechUtility.createUtility(this, SpeechConstant.APPID + "=******"); 注意这里的APPID,不要丢了这个“=”号。

4、在使用到语音播放的地方,初始化语音引擎:
private SpeechSynthesizer mTts = SpeechSynthesizer.createSynthesizer(context, mTtsInitListener);

5、初始化中有两个参数,第一个是上下文,第二个是初始化回调:

private InitListener mTtsInitListener = new InitListener() {
        @Override
        public void onInit(int code) {
            if (code != ErrorCode.SUCCESS) {
                Toast.showToast(context, "语音初始化失败,错误码:" + code);
            } else {
                AppLog.i("语音初始化成功");
                // 初始化成功,之后可以调用startSpeaking方法
                // 注:有的开发者在onCreate方法中创建完合成对象之后马上就调用startSpeaking进行合成,
                // 正确的做法是将onCreate中的startSpeaking调用移至这里
            }
        }
    };

6、播放:

 public void playVoice(String msg) {
        mTts.setParameter(SpeechConstant.VOICE_NAME, "xiaoyan");
        mTts.setParameter(SpeechConstant.SPEED, "60");   //播放速度
        mTts.setParameter(SpeechConstant.VOLUME, "80");   //播放音量
        mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);   //引擎类型
        mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, "./sdcard/iflyket.pcm");    //声音文件地址
        mTts.startSpeaking(msg, mTtsListener);
    }

这里面可以配置播放人声(SpeechConstant.VOICE_NAME)、声音大小(peechConstant.VOLUME)、语速(SpeechConstant.SPEED),如果你发现你选择的人声无法通过手机系统声音控制声音大小时,将人声换成系统默认的“xiaoyan”,在mTts.startSpeaking(msg, mTtsListener); 配置中,第一个参数就是要播放的内容,第二个是播放回调:

private SynthesizerListener mTtsListener = new SynthesizerListener() {

        @Override
        public void onSpeakBegin() {
            // AppLog.i("onSpeakBegin");

        }

        @Override
        public void onSpeakPaused() {
            //  AppLog.i("onSpeakPaused");

        }

        @Override
        public void onSpeakResumed() {
            // AppLog.i("onSpeakResumed");
        }

        @Override
        public void onBufferProgress(int percent, int beginPos, int endPos, String info) {
        }

        @Override
        public void onSpeakProgress(int percent, int beginPos, int endPos) {

        }

        @Override
        public void onCompleted(SpeechError error) {
            if (error == null) {
                if (!queue.isEmpty()) {
                    presenter.playVoice(queue.poll());
                } else {
                    isFirst = true;
                }
            } else if (error != null) {
                AppLog.i(error.getPlainDescription(true));
            }
        }

        @Override
        public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
            // 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
            // 若使用本地能力,会话id为null
            //  if (SpeechEvent.EVENT_SESSION_ID == eventType) {
            //      String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
            //      Log.d(TAG, "session id =" + sid);
            //  }
        }
    };

播放回调看法不言而喻了。

二、重点来了 解决播放跳跃问题:

1、首先来看下这个场景:比如有一个收银系统消息接受终端,每收一笔钱都播放一条“收款成功xxx元”,如果收银速度过款人客流大,当还没播放完第一条语音的时候,第二次收银已经结束了,此时一般情况就会每次播放都直接调用playVioce(String msg)这个这个方法,此时第一条没完,知己播放第二条了。还有一个情景就是,集成高德地图,语音是要自己合成的,同样用科大讯飞来播放路况,当路况复杂的情况下会出现一样的问题。

2、解决问题:解决这个问题的方法就是使用队列:

Queue<String> queue = new LinkedList<String>();

在我的项目的场景就是,每次收款一次,都会受到一次推送一串String,我要播放的就是这条String

//EvenBus接受推送过来的消息广播
 @Subscribe(threadMode = ThreadMode.MAIN)
    public void getMessage(PushEvent pushEvent) {
            Log.i("tag","收款:" + pushEvent.getMessage());
            String msg = pushEvent.getMessage();
            queue.offer(msg);
            //此处需要将语音添加到队列,保证一条播放完再播放下一条
            //必须第一次收到消息才能在这播放,以后每次收到消息将消息添加到队列,每次播放完后去队列里面取;
            if (isFirst) {
                playVoice(queue.poll());
                //保证播放方法这里只执行第一次
                isFirst = !isFirst;
            }
    }

明白的童鞋知道我这里使用的EvenBus来接受推送的消息,可以忽略。将推送过来的消息,放到队列queue中,注意,这里使用的是queue.offer(msg); offer()这个方法,队列添加还有add()这个方法,这两个方法的不同之处就是offer在添加失败的时候不抛异常,所以不建议使用add()方法。使用
queue.poll()来出去队列中的数据,队列是先进先出的,每次poll都会将该条消息从队列中清除。
这里使用的一个变量isFirst来控制播放,只有初次播放的时候才在getMessage()中使用playVoice()方法,这里解决了每次收到消息后都会触发getMessage()这个方法里面的playVoice()。如果每次收到推送消息都调用playVoice(queue.poll());方法会导致少播放一条消息。

在播放回调的onCompleted()方法中继续取出队列里的消息播放,

  @Override
        public void onCompleted(SpeechError error) {
            if (error == null) {
                if (!queue.isEmpty()) {
                    presenter.playVoice(queue.poll());
                } else {
                    isFirst = true;
                }
            } else if (error != null) {
                AppLog.i(error.getPlainDescription(true));
            }
        }

看代码,当队列不为空的时候就继续取出队列里的消息播放,知道为空的时候将isFirst 置为true,否则在全部播放完后不能再次播放了。

三、这样就很完美的解决了播放跳跃问题,这样,无论有多少条消息,多频繁,每次播放都从队列里面取出,这样就达到了播放完一条再播放下一条的问题了。

四、由于这个例子是从自己项目中抽取出来的,没办法上源码,抱歉,写的不好的话请多指正!

猜你喜欢

转载自blog.csdn.net/wb_001/article/details/78561227
今日推荐