Android移动开发-在Android项目里集成讯飞语音识别与合成的实现

“人工智能”是今年比较火的一个名词,甚至大多数人认为“人工智能”是继“IT互联网”之后一个新的浪潮,将会是本世纪最高科技的水准。个人觉得,目前国内语音识别做的比较好的是科大讯飞,图像识别(或人脸识别)做的比较好的是face++(旷视)。

那么,如何在Android项目里集成讯飞语音识别与合成呢?
首先在讯飞语音的开发平台网址:http://www.xfyun.cn/ ,然后在平台注册成为开发者后在点击“控制台”——左边导航栏的“创建新应用”,打开应用创建页面,如下图所示:

Android移动开发-在Android项目里集成讯飞语音识别与合成的实现_第1张图片

填写各项应用信息,并勾选“我已阅读并接受《讯飞用户协议》”,然后点击“提交”按钮,回到应用信息页面后接着点击“立即开通”开通相应的服务,选择开通“语音听写”和“在线语音合成”服务后并点击“确认”按钮开通语音听写和语音合成的服务,语音听写和语音合成的服务开通后回到应用信息页面,即可看到该测试应用的的已开通的服务列表,同时记下测试应用的Appid,后面会在程序代码里用到。如下图所示:

这里写图片描述

然后在平台单页点击“SDK下载”链接,在下载页面选择“语音听写”和“在线语音合成”服务,选择“Android”平台、选择应用是刚才创建的过应用,然后点击“下载SDK”按钮下载开发包。

  • 注意:集成讯飞语音SDK需要注意以下几点:

1.将Msc.jar、Sunflower.jar导入libs目录下,将libmsc.so整个目录导入src/main/jniLibs目录下。以下这些文件必须来自对应的SDK,如果用别的SDK,运行就会报错“用户校检失败”。

2.自定义一个Application类,在onCreate函数中加入以下代码,注意appid值为创建测试应用时分配到的Appid:

SpeechUtility.createUtility(MainApplication.this, "appid=5a046888");

3.在AndroidMainifest.xml中加入必要的权限和自定义的Application类。


    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    
    <uses-permission android:name="android.permission.INTERNET" />
    
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    
    <uses-permission android:name="android.permission.RECORD_VIDEO" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

    <application
        android:name=".MainApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        ...
         application>
  • 语音识别

  • layout/activity_voice_recognize.xml界面布局代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:orientation="vertical"
    android:padding="5dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="讯飞语音识别"
            android:textColor="@color/black"
            android:textSize="20sp" />

        <Button
            android:id="@+id/xf_recognize_setting"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:text="设置"
            android:textColor="@color/black"
            android:textSize="17sp" />
    RelativeLayout>

    <EditText
        android:id="@+id/xf_recognize_text"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_margin="5dp"
        android:layout_weight="1"
        android:background="@drawable/editext_selector"
        android:gravity="top|left"
        android:padding="5dp"
        android:textColor="@color/black"
        android:textSize="20sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/xf_recognize_start"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="开始"
            android:textColor="@color/black"
            android:textSize="17sp" />

        <Button
            android:id="@+id/xf_recognize_stop"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="停止"
            android:textColor="@color/black"
            android:textSize="17sp" />

        <Button
            android:id="@+id/xf_recognize_cancel"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="取消"
            android:textColor="@color/black"
            android:textSize="17sp" />
    LinearLayout>

    <Button
        android:id="@+id/xf_recognize_stream"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="音频流识别"
        android:textColor="@color/black"
        android:textSize="17sp" />

LinearLayout>
  • VoiceRecognizeActivity.java逻辑代码如下:
package com.fukaimei.speechrecognizer;

import java.util.HashMap;
import java.util.LinkedHashMap;

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

import com.fukaimei.speechrecognizer.util.FucUtil;
import com.fukaimei.speechrecognizer.util.JsonParser;
import com.iflytek.cloud.ErrorCode;
import com.iflytek.cloud.InitListener;
import com.iflytek.cloud.RecognizerListener;
import com.iflytek.cloud.RecognizerResult;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechRecognizer;
import com.iflytek.cloud.ui.RecognizerDialog;
import com.iflytek.cloud.ui.RecognizerDialogListener;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.Toast;

public class VoiceRecognizeActivity extends AppCompatActivity implements OnClickListener {
    private final static String TAG = VoiceRecognizeActivity.class.getSimpleName();
    // 语音听写对象
    private SpeechRecognizer mRecognize;
    // 语音听写UI
    private RecognizerDialog mRecognizeDialog;
    // 用HashMap存储听写结果
    private HashMap mRecognizeResults = new LinkedHashMap();

    private EditText mResultText;
    private SharedPreferences mSharedPreferences;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_voice_recognize);
        // 定义获取录音的动态权限
        soundPermissions();

        mResultText = ((EditText) findViewById(R.id.xf_recognize_text));
        findViewById(R.id.xf_recognize_start).setOnClickListener(this);
        findViewById(R.id.xf_recognize_stop).setOnClickListener(this);
        findViewById(R.id.xf_recognize_cancel).setOnClickListener(this);
        findViewById(R.id.xf_recognize_stream).setOnClickListener(this);
        findViewById(R.id.xf_recognize_setting).setOnClickListener(this);
        mSharedPreferences = getSharedPreferences(VoiceSettingsActivity.PREFER_NAME, Activity.MODE_PRIVATE);

        // 初始化识别无UI识别对象,使用SpeechRecognizer对象,可根据回调消息自定义界面;
        mRecognize = SpeechRecognizer.createRecognizer(this, mInitListener);
        // 初始化听写Dialog,如果只使用有UI听写功能,无需创建SpeechRecognizer
        // 使用UI听写功能,请将assets下文件拷贝到项目中
        mRecognizeDialog = new RecognizerDialog(this, mInitListener);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 退出时释放连接
        mRecognize.cancel();
        mRecognize.destroy();
    }

    @Override
    public void onClick(View v) {
        int ret = 0; // 函数调用返回值
        int resid = v.getId();
        if (resid == R.id.xf_recognize_setting) {  // 进入参数设置页面
            Intent intent = new Intent(this, VoiceSettingsActivity.class);
            intent.putExtra("type", VoiceSettingsActivity.XF_RECOGNIZE);
            startActivity(intent);
        } else if (resid == R.id.xf_recognize_start) {  // 开始听写。如何判断一次听写结束:OnResult isLast=true 或者 onError
            mResultText.setText(null);// 清空显示内容
            mRecognizeResults.clear();
            // 设置参数
            resetParam();
            boolean isShowDialog = mSharedPreferences.getBoolean("show_dialog", true);
            if (isShowDialog) {
                // 显示听写对话框
                mRecognizeDialog.setListener(mRecognizeDialogListener);
                mRecognizeDialog.show();
                showTip("请开始说话………");
            } else {
                // 不显示听写对话框
                ret = mRecognize.startListening(mRecognizeListener);
                if (ret != ErrorCode.SUCCESS) {
                    showTip("听写失败,错误码:" + ret);
                } else {
                    showTip("请开始说话…");
                }
            }
        } else if (resid == R.id.xf_recognize_stop) {  // 停止听写
            mRecognize.stopListening();
            showTip("停止听写");
        } else if (resid == R.id.xf_recognize_cancel) {  // 取消听写
            mRecognize.cancel();
            showTip("取消听写");
        } else if (resid == R.id.xf_recognize_stream) {  // 音频流识别
            mResultText.setText(null);// 清空显示内容
            mRecognizeResults.clear();
            // 设置参数
            resetParam();
            // 设置音频来源为外部文件
            mRecognize.setParameter(SpeechConstant.AUDIO_SOURCE, "-1");
            // 也可以像以下这样直接设置音频文件路径识别(要求设置文件在sdcard上的全路径):
            // mRecognize.setParameter(SpeechConstant.AUDIO_SOURCE, "-2");
            // mRecognize.setParameter(SpeechConstant.ASR_SOURCE_PATH, "sdcard/XXX/XXX.pcm");
            ret = mRecognize.startListening(mRecognizeListener);
            if (ret != ErrorCode.SUCCESS) {
                showTip("识别失败,错误码:" + ret);
            } else {
                byte[] audioData = FucUtil.readAudioFile(this, "retcognize_est.wav");
                if (null != audioData) {
                    showTip("开始音频流识别");
                    // 一次(也可以分多次)写入音频文件数据,数据格式必须是采样率为8KHz或16KHz(本地识别只支持16K采样率,云端都支持),位长16bit,单声道的wav或者pcm
                    // 写入8KHz采样的音频时,必须先调用setParameter(SpeechConstant.SAMPLE_RATE, "8000")设置正确的采样率
                    // 注:当音频过长,静音部分时长超过VAD_EOS将导致静音后面部分不能识别
                    mRecognize.writeAudio(audioData, 0, audioData.length);
                    mRecognize.stopListening();
                } else {
                    mRecognize.cancel();
                    showTip("读取音频流失败");
                }
            }
        }
    }

    //初始化监听器
    private InitListener mInitListener = new InitListener() {
        @Override
        public void onInit(int code) {
            Log.d(TAG, "SpeechRecognizer init() code = " + code);
            if (code != ErrorCode.SUCCESS) {
                showTip("初始化失败,错误码:" + code);
            }
        }
    };

    //听写监听器
    private RecognizerListener mRecognizeListener = new RecognizerListener() {

        @Override
        public void onBeginOfSpeech() {
            // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
            showTip("开始说话");
        }

        @Override
        public void onError(SpeechError error) {
            // 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。
            // 如果使用本地功能(语记)需要提示用户开启语记的录音权限。
            showTip(error.getPlainDescription(true));
        }

        @Override
        public void onEndOfSpeech() {
            // 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入
            showTip("结束说话");
        }

        @Override
        public void onResult(RecognizerResult results, boolean isLast) {
            Log.d(TAG, results.getResultString());
            printResult(results);
            if (isLast) {
                // TODO 最后的结果
            }
        }

        @Override
        public void onVolumeChanged(int volume, byte[] data) {
            showTip("当前正在说话,音量大小:" + volume);
            Log.d(TAG, "返回音频数据:"+data.length);
        }

        @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);
            //  }
        }
    };

    private void printResult(RecognizerResult results) {
        String text = JsonParser.parseIatResult(results.getResultString());
        String sn = null;
        try {
            JSONObject resultJson = new JSONObject(results.getResultString());
            sn = resultJson.optString("sn");
        } catch (JSONException e) {
            e.printStackTrace();
            return;
        }
        mRecognizeResults.put(sn, text);
        StringBuffer resultBuffer = new StringBuffer();
        for (String key : mRecognizeResults.keySet()) {
            resultBuffer.append(mRecognizeResults.get(key));
        }
        mResultText.setText(resultBuffer.toString());
        mResultText.setSelection(mResultText.length());
    }

    //听写UI监听器
    private RecognizerDialogListener mRecognizeDialogListener = new RecognizerDialogListener() {
        public void onResult(RecognizerResult results, boolean isLast) {
            printResult(results);
        }

        //识别回调错误
        public void onError(SpeechError error) {
            showTip(error.getPlainDescription(true));
        }
    };

    private void showTip(final String str) {
        Toast.makeText(this, str, Toast.LENGTH_LONG).show();
    }

    //参数设置
    public void resetParam() {
        // 清空参数
        mRecognize.setParameter(SpeechConstant.PARAMS, null);
        // 设置听写引擎。TYPE_LOCAL表示本地,TYPE_CLOUD表示云端,TYPE_MIX 表示混合
        mRecognize.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
        // 设置返回结果格式
        mRecognize.setParameter(SpeechConstant.RESULT_TYPE, "json");

        String lag = mSharedPreferences.getString("recognize_language_preference", "mandarin");
        if (lag.equals("en_us")) {  // 设置语言
            mRecognize.setParameter(SpeechConstant.LANGUAGE, "en_us");
        } else {
            mRecognize.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
            // 设置语言区域
            mRecognize.setParameter(SpeechConstant.ACCENT, lag);
        }

        // 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
        mRecognize.setParameter(SpeechConstant.VAD_BOS, mSharedPreferences.getString("recognize_vadbos_preference", "4000"));
        // 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
        mRecognize.setParameter(SpeechConstant.VAD_EOS, mSharedPreferences.getString("recognize_vadeos_preference", "1000"));
        // 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点
        mRecognize.setParameter(SpeechConstant.ASR_PTT, mSharedPreferences.getString("recognize_punc_preference", "1"));
        // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        // 注:AUDIO_FORMAT参数语记需要更新版本才能生效
        mRecognize.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        mRecognize.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory()+"/msc/recognize.wav");
    }

    // 定义录音的动态权限
    private void soundPermissions() {
        if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.RECORD_AUDIO)
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{
                    android.Manifest.permission.RECORD_AUDIO}, 1);
        }
    }

    /**
     * 重写onRequestPermissionsResult方法
     * 获取动态权限请求的结果,再开启录音
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

        } else {
            Toast.makeText(this, "用户拒绝了权限", Toast.LENGTH_SHORT).show();
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

}
  • Demo程序运行效果界面截图如下:

Android移动开发-在Android项目里集成讯飞语音识别与合成的实现_第2张图片 Android移动开发-在Android项目里集成讯飞语音识别与合成的实现_第3张图片 Android移动开发-在Android项目里集成讯飞语音识别与合成的实现_第4张图片

  • 语音合成

  • layout/activity_voice_compose.xml界面布局代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:orientation="vertical"
    android:padding="5dp">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="讯飞语音合成"
            android:textColor="@color/black"
            android:textSize="20sp" />

        <Button
            android:id="@+id/xf_compose_setting"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:text="设置"
            android:textColor="@color/black"
            android:textSize="17sp" />
    RelativeLayout>

    <EditText
        android:id="@+id/xf_compose_text"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_margin="5dp"
        android:layout_weight="1"
        android:background="@drawable/editext_selector"
        android:gravity="top|left"
        android:padding="5dp"
        android:text="@string/compose_source"
        android:textColor="@color/black"
        android:textSize="20sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/xf_compose_person"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="发音人"
            android:textColor="@color/black"
            android:textSize="17sp" />
    LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="horizontal">

        <Button
            android:id="@+id/xf_compose_play"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="开始合成"
            android:textColor="@color/black"
            android:textSize="17sp" />

        <Button
            android:id="@+id/xf_compose_cancel"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="取消"
            android:textColor="@color/black"
            android:textSize="17sp" />
    LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/xf_compose_pause"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="暂停播放"
            android:textColor="@color/black"
            android:textSize="17sp" />

        <Button
            android:id="@+id/xf_compose_resume"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="继续播放"
            android:textColor="@color/black"
            android:textSize="17sp" />
    LinearLayout>

LinearLayout>
  • VoiceComposeActivity.java逻辑代码如下:
package com.fukaimei.speechrecognizer;

import com.iflytek.cloud.ErrorCode;
import com.iflytek.cloud.InitListener;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechSynthesizer;
import com.iflytek.cloud.SynthesizerListener;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.Toast;

public class VoiceComposeActivity extends AppCompatActivity implements OnClickListener {
    private static String TAG = VoiceComposeActivity.class.getSimpleName();
    // 语音合成对象
    private SpeechSynthesizer mCompose;
    // 默认发音人
    private String voicer = "xiaoyan";
    private String[] mCloudVoicersEntries;
    private String[] mCloudVoicersValue;
    // 缓冲进度
    private int mPercentForBuffering = 0;
    // 播放进度
    private int mPercentForPlaying = 0;

    private EditText mResourceText;
    private SharedPreferences mSharedPreferences;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_voice_compose);

        mResourceText = ((EditText) findViewById(R.id.xf_compose_text));
        findViewById(R.id.xf_compose_play).setOnClickListener(this);
        findViewById(R.id.xf_compose_cancel).setOnClickListener(this);
        findViewById(R.id.xf_compose_pause).setOnClickListener(this);
        findViewById(R.id.xf_compose_resume).setOnClickListener(this);
        findViewById(R.id.xf_compose_setting).setOnClickListener(this);
        findViewById(R.id.xf_compose_person).setOnClickListener(this);
        mSharedPreferences = getSharedPreferences(VoiceSettingsActivity.PREFER_NAME, MODE_PRIVATE);

        // 初始化合成对象
        mCompose = SpeechSynthesizer.createSynthesizer(this, mComposeInitListener);
        // 云端发音人名称列表
        mCloudVoicersEntries = getResources().getStringArray(R.array.voicer_cloud_entries);
        mCloudVoicersValue = getResources().getStringArray(R.array.voicer_cloud_values);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 退出时释放连接
        mCompose.stopSpeaking();
        mCompose.destroy();
    }

    @Override
    public void onClick(View v) {
        int resid = v.getId();
        if (resid == R.id.xf_compose_setting) {
            Intent intent = new Intent(this, VoiceSettingsActivity.class);
            intent.putExtra("type", VoiceSettingsActivity.XF_COMPOSE);
            startActivity(intent);
        } else if (resid == R.id.xf_compose_play) {  // 开始合成
            //收到onCompleted 回调时,合成结束、生成合成音频。合成的音频格式:只支持pcm格式
            String text = mResourceText.getText().toString();
            // 设置参数
            setParam();
            int code = mCompose.startSpeaking(text, mComposeListener);
            if (code != ErrorCode.SUCCESS) {
                showTip("语音合成失败,错误码: " + code);
            }
//          //只保存音频不进行播放接口,调用此接口请注释startSpeaking接口
//          //text:要合成的文本,uri:需要保存的音频全路径,listener:回调接口
//          String path = Environment.getExternalStorageDirectory()+"/compose.pcm";
//          int code = mCompose.synthesizeToUri(text, path, mComposeListener);
        } else if (resid == R.id.xf_compose_cancel) {  // 取消合成
            mCompose.stopSpeaking();
        } else if (resid == R.id.xf_compose_pause) {  // 暂停播放
            mCompose.pauseSpeaking();
        } else if (resid == R.id.xf_compose_resume) {  // 继续播放
            mCompose.resumeSpeaking();
        } else if (resid == R.id.xf_compose_person) {  // 选择发音人
            showPresonSelectDialog();
        }
    }

    private int selectedNum = 0;

    //发音人选择
    private void showPresonSelectDialog() {
        new AlertDialog.Builder(this).setTitle("在线合成发音人选项")
                .setSingleChoiceItems(mCloudVoicersEntries, // 单选框有几项,各是什么名字
                        selectedNum, // 默认的选项
                        new DialogInterface.OnClickListener() { // 点击单选框后的处理
                            public void onClick(DialogInterface dialog, int which) { // 点击了哪一项
                                voicer = mCloudVoicersValue[which];
                                if ("catherine".equals(voicer) || "henry".equals(voicer) || "vimary".equals(voicer)
                                        || "Mariane".equals(voicer) || "Allabent".equals(voicer) || "Gabriela".equals(voicer) || "Abha".equals(voicer) || "XiaoYun".equals(voicer)) {
                                    mResourceText.setText(R.string.compose_source_en);
                                } else {
                                    mResourceText.setText(R.string.compose_source);
                                }
                                selectedNum = which;
                                dialog.dismiss();
                            }
                        }).show();
    }

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

    //合成回调监听
    private SynthesizerListener mComposeListener = new SynthesizerListener() {

        @Override
        public void onSpeakBegin() {
            showTip("开始播放");
        }

        @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.compose_toast_format),
//                  mPercentForBuffering, mPercentForPlaying));
        }

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

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

    private void showTip(final String str) {
        Toast.makeText(this, str, Toast.LENGTH_LONG).show();
    }

    //参数设置
    private void setParam() {
        // 清空参数
        mCompose.setParameter(SpeechConstant.PARAMS, null);
        // 根据合成引擎设置相应参数
        mCompose.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
        // 设置在线合成发音人
        mCompose.setParameter(SpeechConstant.VOICE_NAME, voicer);
        //设置合成语速
        mCompose.setParameter(SpeechConstant.SPEED, mSharedPreferences.getString("speed_preference", "50"));
        //设置合成音调
        mCompose.setParameter(SpeechConstant.PITCH, mSharedPreferences.getString("pitch_preference", "50"));
        //设置合成音量
        mCompose.setParameter(SpeechConstant.VOLUME, mSharedPreferences.getString("volume_preference", "50"));
        //设置播放器音频流类型
        mCompose.setParameter(SpeechConstant.STREAM_TYPE, mSharedPreferences.getString("stream_preference", "3"));
        // 设置播放合成音频打断音乐播放,默认为true
        mCompose.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "true");

        // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        // 注:AUDIO_FORMAT参数语记需要更新版本才能生效
        mCompose.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        mCompose.setParameter(SpeechConstant.TTS_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/compose.wav");
    }

}
  • Demo程序运行效果界面截图如下:

Android移动开发-在Android项目里集成讯飞语音识别与合成的实现_第5张图片 Android移动开发-在Android项目里集成讯飞语音识别与合成的实现_第6张图片 Android移动开发-在Android项目里集成讯飞语音识别与合成的实现_第7张图片

  • MainApplication.java逻辑代码如下:
package com.fukaimei.speechrecognizer;

import android.app.Application;
import com.iflytek.cloud.SpeechUtility;

public class MainApplication extends Application {

    @Override
    public void onCreate() {
        // 应用程序入口处调用,避免手机内存过小,杀死后台进程后通过历史intent进入Activity造成SpeechUtility对象为null
        // 如在Application中调用初始化,需要在Mainifest中注册该Applicaiton
        // 注意:此接口在非主进程调用会返回null对象,如需在非主进程使用语音功能,请增加参数:SpeechConstant.FORCE_LOGIN+"=true"
        // 参数间使用半角“,”分隔。
        // 设置你申请的应用appid,请勿在'='与appid之间添加空格及空转义符
        // 注意: appid 必须和下载的SDK保持一致,否则会出现10407错误

        SpeechUtility.createUtility(MainApplication.this, "appid=5a046888");

        // 以下语句用于设置日志开关(默认开启),设置成false时关闭语音云SDK日志打印
        // Setting.setShowLog(false);
        super.onCreate();
    }

}

Demo程序源码下载地址一(GitHub)
Demo程序源码下载地址二(Gitee)

你可能感兴趣的:(Android移动开发随笔)