Home | 简体中文 | 繁体中文 | 杂文 | Github | 知乎专栏 | Facebook | Linkedin | Youtube | 打赏(Donations) | About
知乎专栏

第 27 章 云服务

目录

27.1. 讯飞云
27.1.1. AIUI
27.1.2. 讯飞 TTS
27.1.3. 语音唤醒
27.2. 阿里云
27.2.1. CosyVoice 语音合成

27.1. 讯飞云

27.1.1. AIUI

		
// 写入文本
//                byte[] content= "你好".getBytes();
//                String params = "data_type=text";
//                AIUIMessage msg = new AIUIMessage(AIUIConstant.CMD_WRITE, 0, 0, "tag=write_data_1", content);
//                mAIUIAgent.sendMessage(msg);

                AIUIMessage aiuiMessage = new AIUIMessage(0, 0, 0, "", null);
                aiuiMessage.msgType = AIUIConstant.CMD_WRITE;
                aiuiMessage.arg1 = 0;
                aiuiMessage.arg2 = 0;
                // 在输入参数中设置tag,则对应结果中也将携带该tag,可用于关联输入输出
                aiuiMessage.params = "data_type=text,tag=text-tag";
                aiuiMessage.data = "天气".getBytes(StandardCharsets.UTF_8);
                mAIUIAgent.sendMessage(aiuiMessage);		
		
	

27.1.1.1. AIUIPlayer

		
package cn.netkiller.aiui;

import android.content.Context;
import android.util.Log;

import androidx.annotation.NonNull;

import com.iflytek.aiui.player.common.data.MetaItem;
import com.iflytek.aiui.player.core.AIUIPlayer;
import com.iflytek.aiui.player.core.PlayState;
import com.iflytek.aiui.player.core.PlayerListener;
import com.iflytek.aiui.player.players.KuwoMusicRemote;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function2;

public class MusicSkillComponent {
    private static final String TAG = MusicSkillComponent.class.getSimpleName();
    private static MusicSkillComponent musicSkillComponent = null;
    private static String kuwoParams = null;
    private static int playListIndex = 0; //当前播放歌曲序号
    private static JSONArray mPlayList = null; //播放列表
    private static AIUIPlayer mAIUIPlayer = null;
    private static KuwoMusicRemote kuwoRemote = null;
    private final Context context;
    private final PlayerListener mPlayListener = new PlayerListener() {
        @Override
        public void onMediaUrl(@NonNull JSONObject jsonObject) {
            Log.d(TAG, "onMediaUrl: " + jsonObject);
        }

        @Override
        public void onPlayerReady() {
            Log.i(TAG, "onPlayerReady()");
        }

        @Override
        public void onStateChange(@NonNull PlayState state) {
            Log.i(TAG, "onStateChange: PlayState is" + state);
            switch (state) {
                case READY:
                    Log.d(TAG, "AIUIPlayer 播放准备就绪");
                    break;
                case PLAYING:
                    Log.d(TAG, "播放");
                    break;
                case PAUSED:
                    Log.d(TAG, "停止");
                    break;
                case LOADING:
                    Log.d(TAG, "音乐加载中……");
                    break;
                case COMPLETE:
                    Log.d(TAG, "继续");
                    startPlayMusic();
                    break;
                case IDLE:
                    long currentPosition = mAIUIPlayer.getCurrentPosition();
                    Log.d(TAG, String.format("CurrentPosition: %l", currentPosition));
                    break;
                case ERROR:
                    Log.d(TAG, "播放出错");
                    break;
                default:
                    break;
            }
        }

        @Override
        public void onMediaChange(@NonNull MetaItem metaItem) {
            String songName = metaItem.getTitle();      //歌名
            String author = metaItem.getAuthor();      //作者
            playListIndex = getPlayIndex(songName);
            Log.d(TAG, "onMediaChange: " + metaItem);
        }

        @Override
        public void onError(int code, @NonNull String info) {
            Log.e(TAG, "onError 播放出错:" + code + " ,错误信息为:" + info);
            // 真实错误码需要从 info 中解析   "track link failed code: 40006 description:"
            if (info.isEmpty()) {
                if (code == 200001) {
                    Log.d(TAG, "歌曲\"" + "" + "\"播放出错: " + code + "\nINFO:产品未通过酷我验收,仅支持获取奇数id资源\n");
                    if (!mAIUIPlayer.next()) {
                    }
                }
            }

        }

        @Override
        public void onPlayerRelease() {

        }
    };

    public MusicSkillComponent(Context context) {
        this.context = context;
        initSDK();
    }

    public synchronized static MusicSkillComponent getInstance(Context context) {
        if (musicSkillComponent == null) {
            musicSkillComponent = new MusicSkillComponent(context);
        }
        return musicSkillComponent;
    }

    public boolean previous() {
        if (mAIUIPlayer != null) {
            return mAIUIPlayer.previous();
        }
        return false;
    }

    public boolean next() {
        if (mAIUIPlayer != null) {
            return mAIUIPlayer.next();
        }
        return false;
    }

    public void pause() {
        if (mAIUIPlayer == null) {
            return;
        }
        if (mAIUIPlayer.getCurrentState() == PlayState.PLAYING) {
            mAIUIPlayer.pause();
        }
    }

    public void resume() {
        if (mAIUIPlayer == null) {
            return;
        }
        if (mAIUIPlayer.getCurrentState() == PlayState.PAUSED) {
            mAIUIPlayer.resume();
        }
    }

    public boolean play(@NonNull JSONObject object) {

//        Log.d(TAG, object.toString());

        try {

            JSONArray musics = object.getJSONArray("result");
            if (null != musics) {
                mPlayList = musics;
                playListIndex = 0;
            }
            return startPlayMusic();

        } catch (JSONException e) {
            e.printStackTrace();
        }

        return false;
    }

    public void initSDK() {
        //TODO 开发者需要实现生成sn的代码,参考:https://www.yuque.com/iflyaiui/zzoolv/tgftb5
        //注意事项1: sn每台设备需要唯一!!!!WakeupEngine的sn和AIUI的sn要一致
        //注意事项2: 获取的值要保持稳定,否则会重复授权,浪费授权量
        String serialNumber = "test";
        String appId = "c84e1ddb";
        String appKey = "7a1583c3190b83fe4d62573ee9cfbfc1";

        //TODO 设置酷我音乐SDK设置相关参数,appid和appkey请使用自己的进行开发,并且与aiui.cfg一致
        kuwoParams = "appId=" + appId + ",appKey=" + appKey + "," + "serialNumber=" + serialNumber + ",deviceModel=" + serialNumber + ",userId=" + serialNumber;


        if (null == kuwoRemote) {
            kuwoRemote = new KuwoMusicRemote(kuwoParams);
            // 酷我SDK日志开关 :true 打开,false 关闭
            kuwoRemote.setDebug(false);

            if (null != kuwoRemote) {
                Log.i(TAG, "KuwoRemote 初始化成功");
            }

        }

        if (null == mAIUIPlayer) {
            mAIUIPlayer = new AIUIPlayer(context, kuwoParams);
            // 播放前焦点占用设置
            mAIUIPlayer.setParameter("customAudioFocus", "true");
            // 回调信息设置
            mAIUIPlayer.addListener(mPlayListener);
            // AIUIPlayer SDK调试日志设置 :true 打开,false 关闭
            mAIUIPlayer.setDebug(false);
            // 初始化播放器
            mAIUIPlayer.initialize();
            Log.i(TAG, "AIUIPlayer 初始化成功");

        }
    }

    public void release() {
        mPlayList = null;
        if (null != mAIUIPlayer) {
            mAIUIPlayer.release();
            mAIUIPlayer = null;
        }
        if (null != kuwoRemote) {
            kuwoRemote.destroy();
            kuwoRemote = null;
        }
    }

    private void activate() {
//        if (isActivated) {
////            showToast("当前设备已激活酷我音乐");
//            return;
//        }
        kuwoRemote.active(() -> {
            Log.i(TAG, "激活成功");
            return null;
        }, (errCode, errInfo) -> {
            Log.i(TAG, "激活失败,错误码为:" + errCode + " ,信息为: " + errInfo);
            return null;
        });
    }

    private void login() {
        //酷我不强制用户登陆,开发者自己实现登陆代码,可参考下方酷狗音乐代码,改一下接口
//        Intent intent = new Intent(KuwoDemo.this, LoginActivity.class);
//        startActivityForResult(intent, 3);
    }

    private void logout() {
        kuwoRemote.logout(new Function0<Unit>() {
            @Override
            public Unit invoke() {
                Log.i(TAG, "酷我账号退出成功");
//                LoginBtn.setText("手机登陆");
                return null;
            }
        }, new Function2<Integer, String, Unit>() {
            @Override
            public Unit invoke(Integer errCode, String errInfo) {
                Log.i(TAG, "酷我账号退出失败,错误码为:" + errCode + " ,信息为: " + errInfo);
                return null;
            }
        });
    }

    public void stop() {
        if (mAIUIPlayer != null) {
            mAIUIPlayer.stop();
        }
    }

    private boolean startPlayMusic() {
        if (null == mPlayList || mPlayList.length() == 0) {
            Log.w(TAG, "播放列表为空");
            return false;
        }
        Log.i(TAG, "mPlayList is: " + mPlayList.toString());
        mAIUIPlayer.reset();
        return mAIUIPlayer.play(mPlayList, "musicX", "", false, playListIndex);
    }

    //构建虚假播放信息测试AIUIPlayer SDK播放是否可以正常调用
//    private void mockPlayMusic() {
//        try {
//            JSONArray musiclist = new JSONArray();
//            JSONObject music = new JSONObject();
//            music.put("source", "kuwo");
//            music.put("songname", "天地龙鳞");
//            music.put("itemid", "353833243");
//            musiclist.put(0, music);
//            mPlayList = musiclist;
//        } catch (JSONException e) {
//            e.printStackTrace();
//        }
//
//        if (null == mPlayList || mPlayList.length() == 0) {
////            showToast("播放列表为空");
//            return;
//        }
//        Log.i(TAG, "mPlayList is:\n" + mPlayList.toString());
//        mAIUIPlayer.reset();
//        mAIUIPlayer.play(mPlayList, "musicX", "", false, playListIndex);
//    }

    // 获取当前播放下标
    private int getPlayIndex(String songName) {
        if (mPlayList != null) {
            try {
                String playData = null;
                for (int i = 0; i < mPlayList.length(); i++) {
                    playData = mPlayList.getJSONObject(i).toString();
                    if (playData.contains(songName)) {
                        return i;
                    }
                }
            } catch (JSONException e) {
                e.printStackTrace();
                return 0;
            }
        }
        return 0;
    }
}		
		
		

27.1.1.2. 酷我音乐

获取音乐URL

通过 itemId 获取 audio URL

			
    kuwoRemote.getAudioUrl("26383685", "128kmp3", new Function1<AudioUrl, Unit>() {
        @Override
        public Unit invoke(AudioUrl audioUrl) {
            Log.d(TAG, audioUrl.toString());
            return null;
        }
    }, new Function2<Integer, String, Unit>() {
        @Override
        public Unit invoke(Integer code, String msg) {
            Log.d(TAG, "Code: " + code + ", Msg: " + msg);
            return null;
        }
    });		
			
			

日志输出结果

			
AudioUrl(expiretime=, itemid=26383685, source=kuwo, audiopath=http://other.player.ri01.sycdn.kuwo.cn/6dcbdb125c72dfff3978fe29ab50fdd4/64eeef6f/resource/n2/27/48/2060696053.mp3)			
			
			

27.1.1.3. 控制技能

		
package cn.netkiller.skill;

import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.util.Log;

import com.alibaba.fastjson.JSONObject;

import java.text.NumberFormat;
import java.text.ParseException;

public class ControlSkillComponent {
    private static final String TAG = ControlSkillComponent.class.getName();
    private final Context context;
    private MusicSkillComponent musicSkillComponent = null;

    public ControlSkillComponent(Context context, JSONObject object) {
        this.context = context;
        musicSkillComponent = MusicSkillComponent.getInstance(context);
        Log.d(TAG, "控制技能: " + object);
        String semanticIntent = object.getString("intent");
        switch (semanticIntent) {
            case "PAUSE":
                musicSkillComponent.pause();
                break;
            case "CHOOSE_NEXT":
                musicSkillComponent.next();
                break;
            case "CHOOSE_PREVIOUS":
                musicSkillComponent.previous();
                break;
            case "VOLUME_MINUS":
                this.volume("VOLUME_MINUS");
                break;
            case "VOLUME_PLUS":
                this.volume("VOLUME_PLUS");
                break;
            case "VOLUME_MIN":
                this.volume("VOLUME_MIN");
                break;
            case "VOLUME_MAX":
                this.volume("VOLUME_MAX");
                break;
            case "MUTE":
                this.volume("MUTE");
                break;
            case "VOLUME_SET":

                JSONObject slots = (JSONObject) object.getJSONArray("slots").get(0);
                String stringPercent = slots.getString("value");
                try {
                    double doublePercent = (Double) NumberFormat.getPercentInstance().parse(stringPercent);
//                    int percent = floatPercent.intValue();
                    this.volume(doublePercent);
                    Log.d(TAG, String.valueOf(doublePercent));
                } catch (ParseException e) {
                    Log.e(TAG, e.toString());
                }
                break;
            case "SETTING_OPEN":
                Log.e(TAG, "设置无线网络");
                this.wifi();
                break;
            case "SHUTDOWN":
                break;
            case "RESET":
                break;
        }


    }

    public void wifi() {
        //        context.startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
        Intent intent = new Intent();
        intent.setAction("androad.network.wlan");
        intent.putExtra("message", "");
        context.sendBroadcast(intent);
    }

    private void volume(String control) {
        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
//        int minVolume = audioManager.getStreamMinVolume(AudioManager.STREAM_MUSIC);
        int minVolume = 10;
        int stepVolume = 5;
        int currentMusicVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        int currentTTSVolume = audioManager.getStreamVolume(AudioManager.STREAM_ALARM);


        switch (control) {
            case "VOLUME_MINUS": //步进减小
                currentMusicVolume -= stepVolume;
                if (currentMusicVolume < minVolume) {
                    currentMusicVolume = minVolume;
                }
                currentTTSVolume -= stepVolume;
                if (currentTTSVolume < minVolume) {
                    currentTTSVolume = minVolume;
                }
                break;
            case "VOLUME_PLUS": //步进累加
                currentMusicVolume += stepVolume;
                if (currentMusicVolume >= maxVolume) {
                    currentMusicVolume = maxVolume;
                }
                currentTTSVolume += stepVolume;
                if (currentTTSVolume > maxVolume) {
                    currentTTSVolume = maxVolume;
                }
                break;

            case "VOLUME_MAX": // 最大
                currentMusicVolume = currentTTSVolume = maxVolume;

                break;
            case "VOLUME_MIN": //最小
                currentMusicVolume = currentTTSVolume = minVolume;

                break;
            case "MUTE": //静音
                currentMusicVolume = currentTTSVolume = minVolume;
                break;

        }
        audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentMusicVolume, AudioManager.FLAG_SHOW_UI);
        audioManager.setStreamVolume(AudioManager.STREAM_ALARM, currentTTSVolume, AudioManager.FLAG_PLAY_SOUND);
        Log.d(TAG, String.format("volume: currentMusicVolume=%s, currentTTSVolume=%s, maxVolume=%s", currentMusicVolume, currentTTSVolume, maxVolume));
    }

    private void volume(double percent) {
        if (percent < 0.3) {
            return;
        }
        AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        int currentMusicVolume, currentTTSVolume;
        currentMusicVolume = currentTTSVolume = (int) (maxVolume * percent);
        audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, currentMusicVolume, AudioManager.FLAG_SHOW_UI);
        audioManager.setStreamVolume(AudioManager.STREAM_ALARM, currentTTSVolume, AudioManager.FLAG_PLAY_SOUND);
        Log.d(TAG, String.format("volume: currentMusicVolume=%s, currentTTSVolume=%s, maxVolume=%s", currentMusicVolume, currentTTSVolume, maxVolume));
    }
}		
		
		

27.1.1.4. 唤醒词

手工唤醒
			
AiuiEngine.MSG_wakeup(EngineConstants.WAKEUPTYPE_VOICE);			
			
			

27.1.1.5. 汉字转拼音

		
implementation 'com.belerweb:pinyin4j:2.5.1'		
		
		
		
package cn.netkiller.ai.utils;

import android.util.Log;

import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;

public class Pinyin {
    private static final String TAG = Pinyin.class.getName();

    public static String toPinyin(String hanzi) {
        char[] chars = hanzi.trim().toCharArray();
        String hanyupinyin = "";

        //输出格式设置
        HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat();
        /**
         * 输出大小写设置
         *
         * LOWERCASE:输出小写
         * UPPERCASE:输出大写
         */
        defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);

        /**
         * 输出音标设置
         *
         * WITH_TONE_MARK:直接用音标符(必须设置WITH_U_UNICODE,否则会抛出异常)
         * WITH_TONE_NUMBER:1-4数字表示音标
         * WITHOUT_TONE:没有音标
         */
//        defaultFormat.setToneType(HanyuPinyinToneType.WITH_TONE_MARK); //  必须设置WITH_U_UNICODE,否则会抛出异常
        defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);

        /**
         * 特殊音标ü设置
         *
         * WITH_V:用v表示ü
         * WITH_U_AND_COLON:用"u:"表示ü
         * WITH_U_UNICODE:直接用ü
         */
//        defaultFormat.setVCharType(HanyuPinyinVCharType.WITH_U_UNICODE);
        defaultFormat.setVCharType(HanyuPinyinVCharType.WITH_V);

        // 中文的正则表达式
        String hanziRegex = "[\\u4E00-\\u9FA5]+";

        try {
            for (int i = 0; i < chars.length; i++) {
                // 判断为中文,则转换为汉语拼音
                if (String.valueOf(chars[i]).matches(hanziRegex)) {
                    hanyupinyin += PinyinHelper
                            .toHanyuPinyinStringArray(chars[i], defaultFormat)[0];
                } else {
                    // 不为中文,则不转换
                    hanyupinyin += chars[i];
                }
            }
        } catch (BadHanyuPinyinOutputFormatCombination e) {
            Log.e(TAG, "字符不能转成汉语拼音");
        }

        return hanyupinyin;
    }

}		
		
		

27.1.2. 讯飞 TTS

27.1.2.1. 设置日志输出级别

		
Setting.setLogLevel(Setting.LOG_LEVEL.low);		
		
		

27.1.2.2. 流式语音合成

		
package com.iflytek.mscv5plusdemo;

import androidx.appcompat.app.AppCompatActivity;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
import android.widget.Toast;

import com.iflytek.cloud.ErrorCode;
import com.iflytek.cloud.InitListener;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechEvent;
import com.iflytek.cloud.SpeechSynthesizer;
import com.iflytek.cloud.SynthesizerListener;
import com.iflytek.speech.setting.TtsSettings;

public class TestActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        tts();
    }

    private InitListener initListener = new InitListener() {
        @Override
        public void onInit(int code) {
            Log.d("TAG", "InitListener init() code = " + code);
            if (code != ErrorCode.SUCCESS) {
                showTip("初始化失败,错误码:" + code + ",请点击网址https://www.xfyun.cn/document/error-code查询解决方案");

            } else {
                // 初始化成功,之后可以调用startSpeaking方法
                // 注:有的开发者在onCreate方法中创建完合成对象之后马上就调用startSpeaking进行合成,
                // 正确的做法是将onCreate中的startSpeaking调用移至这里
            }
        }
    };
    private SpeechSynthesizer speechSynthesizer;

    private void setParam() {
//        Log.d("", String.valueOf(speechSynthesizer.isSpeaking()));
        // 清空参数
        speechSynthesizer.setParameter(SpeechConstant.PARAMS, null);
        //设置合成
//        if (mEngineType.equals(SpeechConstant.TYPE_CLOUD)) {
        //设置使用云端引擎
        speechSynthesizer.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
        //设置发音人
        speechSynthesizer.setParameter(SpeechConstant.VOICE_NAME, "xiaoyan");

//        } else if (mEngineType.equals(SpeechConstant.TYPE_LOCAL)) {
//            //设置使用本地引擎
//            speechSynthesizer.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
//            //设置发音人资源路径
//            speechSynthesizer.setParameter(ResourceUtil.TTS_RES_PATH, getResourcePath());
//            //设置发音人
//            speechSynthesizer.setParameter(SpeechConstant.VOICE_NAME, voicerLocal);
//        } else {
//            speechSynthesizer.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_XTTS);
//            //设置发音人资源路径
//            speechSynthesizer.setParameter(ResourceUtil.TTS_RES_PATH, getResourcePath());
//            //设置发音人
//            speechSynthesizer.setParameter(SpeechConstant.VOICE_NAME, voicerXtts);
//        }
        //mTts.setParameter(SpeechConstant.TTS_DATA_NOTIFY,"1");//支持实时音频流抛出,仅在synthesizeToUri条件下支持
        //设置合成语速
//        speechSynthesizer.setParameter(SpeechConstant.SPEED, mSharedPreferences.getString("speed_preference", "50"));
//        //设置合成音调
//        speechSynthesizer.setParameter(SpeechConstant.PITCH, mSharedPreferences.getString("pitch_preference", "50"));
//        //设置合成音量
//        speechSynthesizer.setParameter(SpeechConstant.VOLUME, mSharedPreferences.getString("volume_preference", "50"));
//        //设置播放器音频流类型
//        speechSynthesizer.setParameter(SpeechConstant.STREAM_TYPE, mSharedPreferences.getString("stream_preference", "3"));
        //	mTts.setParameter(SpeechConstant.STREAM_TYPE, AudioManager.STREAM_MUSIC+"");

        // 设置播放合成音频打断音乐播放,默认为true
        speechSynthesizer.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "true");

        // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        speechSynthesizer.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        speechSynthesizer.setParameter(SpeechConstant.TTS_AUDIO_PATH,
                getExternalFilesDir("msc").getAbsolutePath() + "/tts.pcm");
    }

    public void tts() {

        speechSynthesizer = SpeechSynthesizer.createSynthesizer(this, initListener);


        // 设置参数
//        setParam();
//        Log.d(TAG, "准备点击: " + System.currentTimeMillis());
        String text = "你好小虎";


        setParam();
        speechSynthesizer.stopSpeaking();
        int code = speechSynthesizer.startSpeaking(text, synthesizerListener);
//			/**
//			 * 只保存音频不进行播放接口,调用此接口请注释startSpeaking接口
//			 * text:要合成的文本,uri:需要保存的音频全路径,listener:回调接口
//			*/
//			String path = getExternalFilesDir("msc").getAbsolutePath() + "/tts.pcm";
//			int code = mTts.synthesizeToUri(text, path, mTtsListener);

        if (code != ErrorCode.SUCCESS) {
            showTip("语音合成失败,错误码: " + code + ",请点击网址https://www.xfyun.cn/document/error-code查询解决方案");
        }

//
//
//        mTts.pauseSpeaking();
//
//        mTts.resumeSpeaking();

    }

    SynthesizerListener synthesizerListener = new SynthesizerListener() {

        @Override
        public void onSpeakBegin() {
            showTip("开始播放");
//                Log.d(TtsDemo.TAG, "开始播放:" + System.currentTimeMillis());
        }

        @Override
        public void onSpeakPaused() {
            showTip("暂停播放");
        }

        @Override
        public void onSpeakResumed() {
            showTip("继续播放");
        }

        @Override
        public void onBufferProgress(int percent, int beginPos, int endPos,
                                     String info) {
            // 合成进度
//                mPercentForBuffering = percent;
//                showTip(String.format(getString(R.string.tts_toast_format),mPercentForBuffering, mPercentForPlaying));
        }

        @Override
        public void onSpeakProgress(int percent, int beginPos, int endPos) {
            // 播放进度
//                mPercentForPlaying = percent;
//                showTip(String.format(getString(R.string.tts_toast_format),
//                        mPercentForBuffering, mPercentForPlaying));
        }

        @Override
        public void onCompleted(SpeechError error) {
            if (error == null) {
                showTip("播放完成");
            } else {
                showTip(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_AUDIO_URL);
//                    Log.d(TAG, "session id =" + sid);
            }

            //实时音频流输出参考
			/*if (SpeechEvent.EVENT_TTS_BUFFER == eventType) {
				byte[] buf = obj.getByteArray(SpeechEvent.KEY_EVENT_TTS_BUFFER);
				Log.e("MscSpeechLog", "buf is =" + buf);
			}*/
        }
    };

    private void showTip(final String str) {
        Toast mToast;
//        if (mToast != null) {
//            mToast.cancel();
//        }
        mToast = Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT);
        mToast.show();
    }


}		
		
		

27.1.3. 语音唤醒

27.1.3.1. 范例

		
package cn.netkiller.album.religion.ai;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import com.iflytek.cloud.RequestListener;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechEvent;
import com.iflytek.cloud.VoiceWakeuper;
import com.iflytek.cloud.WakeuperListener;
import com.iflytek.cloud.WakeuperResult;
import com.iflytek.cloud.util.ResourceUtil;

import java.nio.charset.StandardCharsets;

import cn.netkiller.album.religion.ContextHolder;
import cn.assets.album.religion.R;

public class Wakeup {
    private static final String TAG = Wakeup.class.getSimpleName();
    private final int curThresh = 1450;
    private final String threshStr = "门限值:";
    private final String keep_alive = "1";
    private final String ivwNetMode = "0";
    private final Context context;
    // 查询资源请求回调监听
    private final RequestListener requestListener = new RequestListener() {
        @Override
        public void onEvent(int eventType, Bundle params) {
            // 以下代码用于获取查询会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
            //if(SpeechEvent.EVENT_SESSION_ID == eventType) {
            // 	Log.d(TAG, "sid:"+params.getString(SpeechEvent.KEY_EVENT_SESSION_ID));
            //}
        }

        @Override
        public void onCompleted(SpeechError error) {
            if (error != null) {
                Log.d(TAG, "error:" + error.getErrorCode());
                showTip(error.getPlainDescription(true));
            }
        }

        @Override
        public void onBufferReceived(byte[] buffer) {
            try {
                String resultInfo = new String(buffer, StandardCharsets.UTF_8);
                Log.d(TAG, "resultInfo:" + resultInfo);

//                JSONTokener tokener = new JSONTokener(resultInfo);
//                JSONObject object = new JSONObject(tokener);
//
//                int ret = object.getInt("ret");
//                if (ret == 0) {
//                    String uri = object.getString("dlurl");
//                    String md5 = object.getString("md5");
//                    Log.d(TAG, "uri:" + uri);
//                    Log.d(TAG, "md5:" + md5);
//                    showTip("请求成功");
//                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    };
    private VoiceWakeuper voiceWakeuper;
    // 唤醒结果内容
    private String resultString;
    private final WakeuperListener wakeuperListener = new WakeuperListener() {

        @Override
        public void onResult(WakeuperResult result) {
            Log.d(TAG, "onResult");

            try {
                String text = result.getResultString();
                Log.d(TAG, text);
//                JSONObject object;
//                object = new JSONObject(text);
//                StringBuffer buffer = new StringBuffer();
//                buffer.append("【RAW】 " + text);
//                buffer.append("\n");
//                buffer.append("【操作类型】" + object.optString("sst"));
//                buffer.append("\n");
//                buffer.append("【唤醒词id】" + object.optString("id"));
//                buffer.append("\n");
//                buffer.append("【得分】" + object.optString("score"));
//                buffer.append("\n");
//                buffer.append("【前端点】" + object.optString("bos"));
//                buffer.append("\n");
//                buffer.append("【尾端点】" + object.optString("eos"));
//                resultString = buffer.toString();
                showTip(text);
            } catch (Exception e) {
                resultString = "结果解析出错";
                e.printStackTrace();
            }

        }

        @Override
        public void onError(SpeechError error) {
            showTip(error.getPlainDescription(true));
        }

        @Override
        public void onBeginOfSpeech() {
        }

        @Override
        public void onEvent(int eventType, int isLast, int arg2, Bundle obj) {
            // EVENT_RECORD_DATA 事件仅在 NOTIFY_RECORD_DATA 参数值为 真 时返回
            if (eventType == SpeechEvent.EVENT_RECORD_DATA) {
                final byte[] audio = obj.getByteArray(SpeechEvent.KEY_EVENT_RECORD_DATA);
                Log.i(TAG, "ivw audio length: " + audio.length);
            }
        }

        @Override
        public void onVolumeChanged(int volume) {

        }
    };
    // 设置门限值 : 门限值越低越容易被唤醒


    public Wakeup() {
        this.context = ContextHolder.getContext();
//        String param = "appid=" + context.getString(R.string.app_id) + "," + SpeechConstant.ENGINE_MODE + "=" + SpeechConstant.MODE_MSC;
//        SpeechUtility.createUtility(context, param);

        voiceWakeuper = VoiceWakeuper.createWakeuper(context, null);
        if (null == voiceWakeuper) {
            // 创建单例失败,与 21001 错误为同样原因,参考 http://bbs.xfyun.cn/forum.php?mod=viewthread&tid=9688
            Toast.makeText(context
                    , "创建对象失败,请确认 libmsc.so 放置正确,\n 且有调用 createUtility 进行初始化"
                    , Toast.LENGTH_LONG).show();
        } else {
            Toast.makeText(context, "准备唤醒", Toast.LENGTH_LONG).show();
            // 清空参数
            voiceWakeuper.setParameter(SpeechConstant.PARAMS, null);
            // 唤醒门限值,根据资源携带的唤醒词个数按照“id:门限;id:门限”的格式传入
            voiceWakeuper.setParameter(SpeechConstant.IVW_THRESHOLD, "0:1450");
            // 设置唤醒模式
            voiceWakeuper.setParameter(SpeechConstant.IVW_SST, "wakeup");
            // 设置持续进行唤醒
            voiceWakeuper.setParameter(SpeechConstant.KEEP_ALIVE, "1");
            // 设置闭环优化网络模式
            voiceWakeuper.setParameter(SpeechConstant.IVW_NET_MODE, "1");
            // 设置唤醒资源路径
            voiceWakeuper.setParameter(SpeechConstant.IVW_RES_PATH, getResource());
            // 设置唤醒录音保存路径,保存最近一分钟的音频
            voiceWakeuper.setParameter(SpeechConstant.IVW_AUDIO_PATH,
                    context.getExternalFilesDir("msc").getAbsolutePath() + "/ivw.wav");
            voiceWakeuper.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
            // 如有需要,设置 NOTIFY_RECORD_DATA 以实时通过 onEvent 返回录音音频流字节
            //voiceWakeuper.setParameter( SpeechConstant.NOTIFY_RECORD_DATA, "1" );
            // 启动唤醒
            /*	voiceWakeuper.setParameter(SpeechConstant.AUDIO_SOURCE, "-1");*/
        }
    }

    public void startListening(WakeuperListener wakeuperListener) {
        //非空判断,防止因空指针使程序崩溃
        voiceWakeuper = VoiceWakeuper.getWakeuper();
        if (voiceWakeuper != null) {
            resultString = "";
            voiceWakeuper.startListening(wakeuperListener);
        } else {
            showTip("唤醒未初始化");
        }
    }

    private void stopListening() {
        voiceWakeuper = VoiceWakeuper.getWakeuper();
        if (voiceWakeuper != null) {
            voiceWakeuper.stopListening();
        }
    }

    public void cancel() {
        // 销毁合成对象
        voiceWakeuper = VoiceWakeuper.getWakeuper();
        if (voiceWakeuper != null) {
            voiceWakeuper.cancel();
        }
    }

    public void destroy() {
        // 销毁合成对象
        voiceWakeuper = VoiceWakeuper.getWakeuper();
        if (voiceWakeuper != null) {
            voiceWakeuper.destroy();
        }
    }

    private String getResource() {
        final String resPath = ResourceUtil.generateResourcePath(context, ResourceUtil.RESOURCE_TYPE.assets, "ivw/" + context.getString(R.string.app_id) + ".jet");
        Log.d(TAG, "resPath: " + resPath);
        return resPath;
    }

    /**
     * 查询闭环优化唤醒资源
     * 请在闭环优化网络模式1或者模式2使用
     */
//    public void queryResource() {
//        int ret = voiceWakeuper.queryResource(getResource(), requestListener);
//        showTip("updateResource ret:" + ret);
//    }
    private void showTip(final String str) {
//        runOnUiThread(new Runnable() {
//            @Override
//            public void run() {

        Toast.makeText(context.getApplicationContext(), str, Toast.LENGTH_SHORT).show();

//            }
//        });
    }
}