当前位置:阳光沙滩 >Android > 查看文章
阿里云优惠码

1.灵犀语音助手分析

同事都去沈阳了,我还在深圳。之前我们在做技术调研的时候,都会对多家平台进行分析。比如说之前我们需要地图,我们则对高德(A)、百度(B)、腾讯(T)进行分析比较。这次的话,不需要比较了。由于投资的关系(没办法,资本才有话语权),我们直接使用灵犀来完成我们的语音处理模块。

snip20170108_8

2.注册灵犀开发者账号

其实注册灵犀开发者账号,也注是注册10086的开发账号。点击这里跳转到登陆的界面,如果没有账号的话,请你注册一下了.

%e5%b1%8f%e5%b9%95%e5%bf%ab%e7%85%a7-2017-01-08-12-05-14

然后创建应用,去下载对应的资料。

snip20170108_2

下面这个图的画,先点击展开,否则是看不到灵犀的呢。

snip20170108_3

最后点击创建,之后就会跳转到配置的界面了。

PS:不知道为什么用mac下载的话,会解压不了,可以有是我的浏览器下载出错了。而用windows电脑下载下来没事,可以用哈。

snip20170108_4

点击下载下来,解压之后呢,就是这么一个目录结构啦:

snip20170108_9

3.Demo项目分析

在给出的例子里头,有两个。一个是灵犀云声纹示例,另外一个是语音示例。

3.1.语音示例

snip20170109_10

这里面的功能列表有:

// Menu 列表
String items[] = { "立刻体验语音听写"
		, "立刻体验语法识别"
		, "立刻体验语义理解"
		, "立刻体验语音合成"
		, "立刻体验语音评测"
		, "立刻体验声纹密码"
		, "立刻体验语音唤醒"
		};

一共有七个,但是呢,后面两个是不有的哈。看下面的代码就知道了:

public void onClick(View view) {
		int tag = Integer.parseInt(view.getTag().toString());
		Intent intent = null;
		switch (tag) {
		case 0:
			// 语音转写
			intent = new Intent(this, IatDemo.class);
			break;
		case 1:
			// 语法识别
			intent = new Intent(this, AsrDemo.class);
			break;
		case 2:
			// 语义理解
			intent = new Intent(this, UnderstanderDemo.class);
			break;
		case 3:
			// 语音合成
			intent = new Intent(this, TtsDemo.class);
			break;
		case 4:
			// 语音评测
			intent = new Intent(this, IseDemo.class);
			break;
		case 5:
			// 声纹
			this.showTip( "见声纹的Demo" );
			break;
		case 6:
			// 唤醒
		default:
			showTip("请申请离线版SDK");
			break;
		}
		
		if (intent != null) {
			startActivity(intent);
		}
	}

那么这里呢,就是点击不同的控件,就跳转到不同功能的界面。我们还是逐一进行分析吧。

3.1.1.语音转写

语音转写,就是获取到话的话,然后转成文字。这里面的话,提供了三种语言,分别是:国语,粤语、英语。

跳转意图对象:

// 语音转写
intent = new Intent(this, IatDemo.class);

下面呢,就是这个Activity的代码啦,看代码,然后我们进行分析。

public class IatDemo extends Activity implements OnClickListener {
    private static String TAG = IatDemo.class.getSimpleName();
    // 语音听写对象
    private SpeechRecognizer mIat;
    // 语音听写UI
    private RecognizerDialog mIatDialog;
    // 用HashMap存储听写结果
    private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();

    private EditText mResultText;
    private Toast mToast;
    private SharedPreferences mSharedPreferences;

    @SuppressLint("ShowToast")
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        //设置布局
        setContentView(R.layout.iatdemo);
        //设置对应控件的点击事件
        initLayout();
        // 初始化识别无UI识别对象
        // 使用SpeechRecognizer对象,可根据回调消息自定义界面;
        mIat = SpeechRecognizer.createRecognizer(this, mInitListener);
        // 初始化听写Dialog,如果只使用有UI听写功能,无需创建SpeechRecognizer
        // 使用UI听写功能,请根据sdk文件目录下的notice.txt,放置布局文件和图片资源
        mIatDialog = new RecognizerDialog(this, mInitListener);
        //创建一个SP
        mSharedPreferences = getSharedPreferences(IatSettings.PREFER_NAME, Activity.MODE_PRIVATE);
        //创建吐司对象
        mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
        mResultText = ((EditText) findViewById(R.id.iat_text));
    }

    /**
     * 初始化Layout。
     */
    private void initLayout() {
        findViewById(R.id.iat_recognize).setOnClickListener(this);
        findViewById(R.id.iat_upload_contacts).setOnClickListener(this);
        findViewById(R.id.iat_upload_userwords).setOnClickListener(this);
        findViewById(R.id.iat_stop).setOnClickListener(this);
        findViewById(R.id.iat_cancel).setOnClickListener(this);
        findViewById(R.id.image_iat_set).setOnClickListener(this);
    }

    int ret = 0; // 函数调用返回值

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            // 进入参数设置页面
            case R.id.image_iat_set:
                Intent intents = new Intent(IatDemo.this, IatSettings.class);
                startActivity(intents);
                break;
            // 开始听写
            // 如何判断一次听写结束:OnResult isLast=true 或者 onError
            case R.id.iat_recognize:
                mResultText.setText(null);// 清空显示内容
                mIatResults.clear();
                // 设置参数
                setParam();
                boolean isShowDialog = mSharedPreferences.getBoolean(
                        getString(R.string.pref_key_iat_show), true);
                if (isShowDialog) {
                    // 显示听写对话框
                    mIatDialog.setListener(recognizerDialogListener);
                    mIatDialog.show();
                    showTip(getString(R.string.text_begin));
                } else {
                    // 不显示听写对话框
                    ret = mIat.startListening(recognizerListener);
                    if (ret != ErrorCode.SUCCESS) {
                        showTip("听写失败,错误码:" + ret);
                    } else {
                        showTip(getString(R.string.text_begin));
                    }
                }
                break;
            // 停止听写
            case R.id.iat_stop:
                mIat.stopListening();
                showTip("停止听写");
                break;
            // 取消听写
            case R.id.iat_cancel:
                mIat.cancel();
                showTip("取消听写");
                break;
            // 上传联系人
            case R.id.iat_upload_contacts:
                showTip(getString(R.string.text_upload_contacts));
                ContactManager mgr = ContactManager.createManager(IatDemo.this,
                        mContactListener);
                mgr.asyncQueryAllContactsName();
                break;
            // 上传用户词表
            case R.id.iat_upload_userwords:
                showTip(getString(R.string.text_upload_userwords));
                String contents = FucUtil.readFile(IatDemo.this, "userwords", "utf-8");
                // 指定引擎类型
                mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
                mIat.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");
                ret = mIat.updateLexicon("userword", contents, lexiconListener);
                if (ret != ErrorCode.SUCCESS)
                    showTip("上传热词失败,错误码:" + ret);
                break;
            default:
                break;
        }
    }

    /**
     * 初始化监听器。
     */
    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 LexiconListener lexiconListener = new LexiconListener() {

        @Override
        public void onLexiconUpdated(String lexiconId, SpeechError error) {
            if (error != null) {
                showTip(error.toString());
            } else {
                showTip(getString(R.string.text_upload_success));
            }
        }
    };

    /**
     * 听写监听器。
     */
    private RecognizerListener recognizerListener = new RecognizerListener() {

        @Override
        public void onBeginOfSpeech() {
            showTip("开始说话");
        }

        @Override
        public void onError(SpeechError error) {
            // Tips:
            // 错误码: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);
        }

        @Override
        public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
        }
    };

    private void printResult(RecognizerResult results) {
        String text = JsonParser.parseIatResult(results.getResultString());

        String sn = null;
        // 读取json结果中的sn字段
        try {
            JSONObject resultJson = new JSONObject(results.getResultString());
            sn = resultJson.optString("sn");
        } catch (JSONException e) {
            e.printStackTrace();
        }

        mIatResults.put(sn, text);

        StringBuffer resultBuffer = new StringBuffer();
        for (String key : mIatResults.keySet()) {
            resultBuffer.append(mIatResults.get(key));
        }

        mResultText.setText(resultBuffer.toString());
        mResultText.setSelection(mResultText.length());
    }

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

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

    };

    /**
     * 获取联系人监听器。
     */
    private ContactListener mContactListener = new ContactListener() {

        @Override
        public void onContactQueryFinish(String contactInfos, boolean changeFlag) {
            // 注:实际应用中除第一次上传之外,之后应该通过changeFlag判断是否需要上传,否则会造成不必要的流量.
            // if(changeFlag) {
            // 指定引擎类型
            mIat.setParameter(SpeechConstant.ENGINE_TYPE,
                    SpeechConstant.TYPE_CLOUD);
            mIat.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");
            ret = mIat.updateLexicon("contact", contactInfos, lexiconListener);
            if (ret != ErrorCode.SUCCESS) {
                showTip("上传联系人失败:" + ret);
            }
        }
    };

    private void showTip(final String str) {
        mToast.setText(str);
        mToast.show();
    }

    /**
     * 参数设置
     */
    public void setParam() {
        // 清空参数
        mIat.setParameter(SpeechConstant.PARAMS, null);

        // 设置返回结果格式
        mIat.setParameter(SpeechConstant.RESULT_TYPE, "json");

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

        // 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
        mIat.setParameter(SpeechConstant.VAD_BOS, mSharedPreferences.getString("iat_vadbos_preference", "4000"));

        // 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
        mIat.setParameter(SpeechConstant.VAD_EOS, mSharedPreferences.getString("iat_vadeos_preference", "1000"));

        // 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点
        mIat.setParameter(SpeechConstant.ASR_PTT, mSharedPreferences.getString("iat_punc_preference", "1"));

        // 设置音频保存路径,保存音频格式仅为pcm,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/cmcc/wavaudio.pcm");

        // 设置听写结果是否结果动态修正,为“1”则在听写过程中动态递增地返回结果,否则只在听写结束之后返回最终结果
        // 注:该参数暂时只对在线听写有效
        mIat.setParameter(SpeechConstant.ASR_DWA, mSharedPreferences.getString("iat_dwa_preference", "0"));
    }

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

    @Override
    protected void onResume() {
        super.onResume();
    }
}

那么这个Activity只做六件事,从UI上来说。分别是:设置,开始语音识别,停止语音识别,取消操作,上传联系人,已及上传词表。一个一个来分析。

1、设置:

设置的话很简单,本质上就是修改了名字为PREFER_NAME = “com.iflytek.setting”的值

/**
 * 听写设置界面
 */
public class IatSettings extends PreferenceActivity implements OnPreferenceChangeListener {
	
	public static final String PREFER_NAME = "com.iflytek.setting";
	private EditTextPreference mVadbosPreference;
	private EditTextPreference mVadeosPreference;
	
	@SuppressWarnings("deprecation")
	public void onCreate(Bundle savedInstanceState) {
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		super.onCreate(savedInstanceState);
		getPreferenceManager().setSharedPreferencesName(PREFER_NAME);
		addPreferencesFromResource(R.xml.iat_setting);
		
		mVadbosPreference = (EditTextPreference)findPreference("iat_vadbos_preference");
		mVadbosPreference.getEditText().addTextChangedListener(new SettingTextWatcher(IatSettings.this,mVadbosPreference,0,10000));
		
		mVadeosPreference = (EditTextPreference)findPreference("iat_vadeos_preference");
		mVadeosPreference.getEditText().addTextChangedListener(new SettingTextWatcher(IatSettings.this,mVadeosPreference,0,10000));
	}
	@Override
	public boolean onPreferenceChange(Preference preference, Object newValue) {
		return true;
	}
}

这样看,其实很奇怪是吧,它的键和值呢,是吧。其实呀,它的值在XML布局里头就已经写了,请看码:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" > 
    
    <ListPreference
        android:key="iat_language_preference"
        android:title="语言设置"
        android:entries="@array/language_entries"
        android:entryValues="@array/language_values"
        android:summary="支持:普通话,粤语,英语 "
        android:defaultValue="mandarin"  />
    
    <EditTextPreference
        android:key="iat_vadbos_preference"   
		android:title="前端点超时" 
		android:dialogTitle="请输入时间(0-10000)ms"
        android:summary="默认值:短信转写5000,其他4000"
		android:defaultValue="5000" />
    
    <EditTextPreference
        android:key="iat_vadeos_preference"   
		android:title="后端点超时" 
		android:dialogTitle="请输入时间(0-10000)ms"
        android:summary="默认值:短信转写1800,其他700 "
		android:defaultValue="1800" />

    <ListPreference
        android:key="iat_punc_preference"
        android:title="标点符号"
        android:entries="@array/punc_entries"
        android:entryValues="@array/punc_values"
        android:summary="默认值:有标点 "
        android:defaultValue="1"  />
    
    <ListPreference
        android:key="iat_dwa_preference"
        android:title="@string/pref_title_iat_dwa"
        android:entries="@array/dwa_entries"
        android:entryValues="@array/punc_values"
        android:summary="默认值:关闭 "
        android:defaultValue="0"  />
    
	<CheckBoxPreference
		android:key="@string/pref_key_iat_show"
		android:title="@string/pref_title_iat_show"
		android:defaultValue="true" />

</PreferenceScreen>

设置这么简单,也没什么好说的,本质上就是修改SP里的值,不过这里我认为还是抽取成一个全局的常量比较好一点。因为拿值的时候也需要使用的嘛。

2、开始语音识别

这个点击以后呢,就会去获取音频,然后转成文字。它的逻辑是这样子的:

 // 清空显示内容
                mResultText.setText(null);
                mIatResults.clear();
                // 设置参数,每次使用前都查进行参数设置,否则设置的参数就无效了。
                setParam();
                //判断是否现实对话框,(这里是从设置的内容里头拿值哈)
                boolean isShowDialog = mSharedPreferences.getBoolean(
                        getString(R.string.pref_key_iat_show), true);
                if (isShowDialog) {
                    // 显示听写对话框,开始监听
                    mIatDialog.setListener(recognizerDialogListener);
                    mIatDialog.show();
                    showTip(getString(R.string.text_begin));
                } else {
                    // 不显示听写对话框,开始监听
                    ret = mIat.startListening(recognizerListener);
                    if (ret != ErrorCode.SUCCESS) {
                        showTip("听写失败,错误码:" + ret);
                    } else {
                        showTip(getString(R.string.text_begin));
                    }
                }

 

这里两个地方要看的,一个是设置,一个是设置监听。

先看设置的内容吧:

/**
     * 参数设置
     */
    public void setParam() {
        // 清空参数
        mIat.setParameter(SpeechConstant.PARAMS, null);
        // 设置返回结果格式
        mIat.setParameter(SpeechConstant.RESULT_TYPE, "json");
        String lag = mSharedPreferences.getString("iat_language_preference",
                "mandarin");
        if (lag.equals("en_us")) {
            // 设置语言
            mIat.setParameter(SpeechConstant.LANGUAGE, "en_us");
        } else {
            // 设置语言
            mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
            // 设置语言区域
            mIat.setParameter(SpeechConstant.ACCENT, lag);
        }

        // 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
        mIat.setParameter(SpeechConstant.VAD_BOS, mSharedPreferences.getString("iat_vadbos_preference", "4000"));

        // 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
        mIat.setParameter(SpeechConstant.VAD_EOS, mSharedPreferences.getString("iat_vadeos_preference", "1000"));

        // 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点
        mIat.setParameter(SpeechConstant.ASR_PTT, mSharedPreferences.getString("iat_punc_preference", "1"));

        // 设置音频保存路径,保存音频格式仅为pcm,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/cmcc/wavaudio.pcm");

        // 设置听写结果是否结果动态修正,为“1”则在听写过程中动态递增地返回结果,否则只在听写结束之后返回最终结果
        // 注:该参数暂时只对在线听写有效
        mIat.setParameter(SpeechConstant.ASR_DWA, mSharedPreferences.getString("iat_dwa_preference", "0"));
    }

接着呢,看设置监听的两个部分。一个是有弹出对话框的,一个是没有。具体要不要弹出来,那就得看设置里的值了。

  // 显示听写对话框,开始监听
                    mIatDialog.setListener(recognizerDialogListener);

这个对话框呢,是SDK内部的,界面已经写好的,只要实现接口就可以获取到语音录入的返回值了。

ret = mIat.startListening(recognizerListener);

这里的话,mIat是语音录入 SpeechRecognizer对象来的。通过它来设置一个监听回调,就可以获取到一个录入结果了。

好啦,语音录入的逻辑就这么多了。简单总结一下语音录入和逻辑吧。

首先,在onCreate的时候创建语音录入对象。

// 使用SpeechRecognizer对象,可根据回调消息自定义界面;
        mIat = SpeechRecognizer.createRecognizer(this, mInitListener);

接着设置参数,这里的话请看上面的设置部分的代码了,已经有注释的了。

然后在录入语音的时候 直接设置坚挺即可。毕竟这里是Demo,要是在实际开发中,大家可以根据业务需求,判断是否需要弹出对话框,如果不需要我们就不创建嘛。

3、停止语音识别

 mIat.stopListening();
 showTip("停止听写");

很简单,就一行代码,调用语音录入对象的停止监听即可。

4、取消操作

 mIat.cancel();
showTip("取消听写");

这里就不多说了哈

5、上传联系人

showTip(getString(R.string.text_upload_contacts));
ContactManager mgr = ContactManager.createManager(IatDemo.this,mContactListener);
mgr.asyncQueryAllContactsName();

ContactManager这个类我也不知道有什么用的,那怎么办呢,看API文档呗。

API文档里头是这么说的:

联系人管理类

联系人管理类,用来获取联系人姓名等数据,用于听写词典等。

联系人管理类仅作为辅助工具查询联系人姓名,用于听写时的词典上传到服务器以提高 听写时对联系人的匹配(见SpeechRecognizer.updateLexicon(java.lang.String, java.lang.String, com.iflytek.cloud.LexiconListener)), 不会上传联系人的电话号码,请放心使用。您也可以自己实现对联系人名字获取,再通过 SpeechRecognizer.updateLexicon(java.lang.String, java.lang.String, com.iflytek.cloud.LexiconListener)上传更新即可。 通过些功能获取系人时,应用需拥有以下权限:
android.Manifest.permission#READ_CONTACTS

结果格式如下:
张三
李四
王五

本类使用单例,调用者使用本类的对象,只需要通过createManager(Context, com.iflytek.cloud.util.ContactManager.ContactListener)创建 一次对象后,便可一直使用该对象,直到通过调用destroy()进行单例对象销毁。调 用者可通过getManager()获取当前已经创建的单例。在销毁本类的单例对象后, 需要先通过createManager(Context, com.iflytek.cloud.util.ContactManager.ContactListener)再次创建单例对象,方可再使用。

从以下版本开始:
version:1073

所以呢,它直接封装好了,你只要调用的话就给你上传联系人了。

还有一个是上传词根表

6、上传词根表

showTip(getString(R.string.text_upload_userwords));
String contents = FucUtil.readFile(IatDemo.this, "userwords", "utf-8");
// 指定引擎类型
mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
mIat.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");
ret = mIat.updateLexicon("userword", contents, lexiconListener);
if (ret != ErrorCode.SUCCESS)
    showTip("上传热词失败,错误码:" + ret);

这里的话是对语音录入对象的参数设置了,我们通过查看api文档可以知道,有那些参数可以设置的哈:

setParameter(java.lang.String key, java.lang.String value)
参数设置 可设置的参数有:
SpeechConstant.NET_TIMEOUT: 网络连接超时时间
SpeechConstant.KEY_SPEECH_TIMEOUT:语音输入超时时间 
SpeechConstant.LANGUAGE:语言
SpeechConstant.ACCENT:语言区域
SpeechConstant.DOMAIN:应用领域
SpeechConstant.CLOUD_GRAMMAR:云端语法ID
SpeechConstant.LOCAL_GRAMMAR:本地语法ID
SpeechConstant.AUDIO_SOURCE:音频源
SpeechConstant.VAD_BOS:前端点超时
SpeechConstant.VAD_EOS:后端点超时
SpeechConstant.SAMPLE_RATE:识别采样率
SpeechConstant.ASR_NBEST:句子级多候选
SpeechConstant.ASR_WBEST:词级多候选
SpeechConstant.ASR_PTT:设置是否有标点符号
SpeechConstant.RESULT_TYPE:识别结果类型
SpeechConstant.ASR_AUDIO_PATH:识别录音保存路径 
SpeechConstant.ENGINE_TYPE:引擎类型; 
ResourceUtil.ASR_RES_PATH:离线资源路径; 
ResourceUtil.ENGINE_START:启动离线引擎; 
ResourceUtil.GRM_BUILD_PATH:离线语法路径; 当前识别支持未压缩的16位,单声道,采样率为16000或8000,字节顺序为 Little-Endian的Windows PCM音频。

那么把语音识别完了,转在文字的结果在那里设置呢,这个从上面的代码可以直接知道,它会在回调里反回结果,我们直接使用即可。

好啦,那么语音识别的就写到这里了,我要去骑自行车了,飞呀飞….好孤单。后面的晚上回来接着再写吧。

其他部分的我们不进行分析,前面是语音转文字的,那么接下来则是文字转语音的

3.2.文字转语音

我们先看它的界面吧,然后从UI上去分析它的功能

屏幕快照 2017-01-17 12.06.13

从UI上可以得知,它的功能是把输入的文字转成语音。先从设置开始说起吧,设置里头呢。

可以设置语速,音调,音量以及音频流类型。

代码如下:


/**
 * 合成设置界面
 */
public class TtsSettings extends PreferenceActivity implements OnPreferenceChangeListener {
	
	public static final String PREFER_NAME = "com.iflytek.setting";
	private EditTextPreference mSpeedPreference;
	private EditTextPreference mPitchPreference;
	private EditTextPreference mVolumePreference;
	
	@SuppressWarnings("deprecation")
	@Override
	public void onCreate(Bundle savedInstanceState) {
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		super.onCreate(savedInstanceState);
		// 指定保存文件名字
		getPreferenceManager().setSharedPreferencesName(PREFER_NAME);
		addPreferencesFromResource(R.xml.tts_setting);
		mSpeedPreference = (EditTextPreference)findPreference("speed_preference");
		mSpeedPreference.getEditText().addTextChangedListener(new SettingTextWatcher(TtsSettings.this,mSpeedPreference,0,100));
		mSpeedPreference.setOnPreferenceChangeListener( this );
		mSpeedPreference.setSummary( mSpeedPreference.getText() );
		
		mPitchPreference = (EditTextPreference)findPreference("pitch_preference");
		mPitchPreference.getEditText().addTextChangedListener(new SettingTextWatcher(TtsSettings.this,mPitchPreference,0,100));
		mPitchPreference.setOnPreferenceChangeListener( this );
		mPitchPreference.setSummary( mPitchPreference.getText() );
		
		mVolumePreference = (EditTextPreference)findPreference("volume_preference");
		mVolumePreference.getEditText().addTextChangedListener(new SettingTextWatcher(TtsSettings.this,mVolumePreference,0,100));
		mVolumePreference.setOnPreferenceChangeListener( this );
		mVolumePreference.setSummary( mVolumePreference.getText() );
	}
	
	@Override
	public boolean onPreferenceChange(Preference preference, Object newValue) {
		if (preference instanceof ListPreference) {
			//获取当前的弹出列表UI对象
			ListPreference listPreference = (ListPreference) preference;
			
			CharSequence[] entries = listPreference.getEntries();
			//获取当前点击的是列表的第几个元素.
			int index = listPreference.findIndexOfValue((String) newValue);

			listPreference.setSummary(entries[index]);
		}else if( preference instanceof EditTextPreference ){
			EditTextPreference editTextPreference = (EditTextPreference)preference;
			editTextPreference.setSummary( (String)newValue );
		}else if( preference instanceof CheckBoxPreference ){
			CheckBoxPreference checkBoxPreference = (CheckBoxPreference)preference;
			checkBoxPreference.setSummary( ((Boolean)newValue).toString() );
		}
		
		return true;
	}		
	
	
}

它的本质也是设置SP的值,在我们进行文字转成语音的时候 ,先获取到设置的值再执行往下的逻辑即可。

以下呢,则是整个语音合成的代码了:


public class TtsDemo extends Activity implements OnClickListener {
	private static String TAG = TtsDemo.class.getSimpleName(); 	
	// 语音合成对象
	private SpeechSynthesizer mTts;

	// 默认发音人
	private String voicer="xiaoyan";
	
	private String[] cloudVoicersEntries;
	private String[] cloudVoicersValue ;
	
	// 缓冲进度
	private int mPercentForBuffering = 0;
	// 播放进度
	private int mPercentForPlaying = 0;
	
	// 云端/本地单选按钮
	private RadioGroup mRadioGroup;
	// 引擎类型
	private String mEngineType = SpeechConstant.TYPE_CLOUD;
	
	private Toast mToast;
	private SharedPreferences mSharedPreferences;
	
	@SuppressLint("ShowToast")
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.ttsdemo);
		
		initLayout();
		// 初始化合成对象
		mTts = SpeechSynthesizer.createSynthesizer(this, mTtsInitListener);
		
		// 云端发音人名称列表
		cloudVoicersEntries = getResources().getStringArray(R.array.voicer_cloud_entries);
		cloudVoicersValue = getResources().getStringArray(R.array.voicer_cloud_values);
				
		mSharedPreferences = getSharedPreferences(TtsSettings.PREFER_NAME, MODE_PRIVATE);
		mToast = Toast.makeText(this,"",Toast.LENGTH_SHORT);
	}

	/**
	 * 初始化Layout。
	 */
	private void initLayout() {
		findViewById(R.id.tts_play).setOnClickListener(TtsDemo.this);
		findViewById(R.id.tts_cancel).setOnClickListener(TtsDemo.this);
		findViewById(R.id.tts_pause).setOnClickListener(TtsDemo.this);
		findViewById(R.id.tts_resume).setOnClickListener(TtsDemo.this);
		findViewById(R.id.image_tts_set).setOnClickListener(TtsDemo.this);

		findViewById(R.id.tts_btn_person_select);
		findViewById(R.id.tts_btn_person_select).setOnClickListener(TtsDemo.this);
		
		mRadioGroup=((RadioGroup) findViewById(R.id.tts_rediogroup));
		mRadioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {

			@Override
			public void onCheckedChanged(RadioGroup group, int checkedId) {
				switch (checkedId) {
				case R.id.tts_radioCloud:
					mEngineType = SpeechConstant.TYPE_CLOUD;
					break;
				case R.id.tts_radioLocal:
					//TODO:本地功能暂不支持
					mEngineType =  SpeechConstant.TYPE_LOCAL;
					break;
				default:
					break;
				}

			}
		} );
	}	

	@Override
	public void onClick(View view) {
		switch(view.getId()) {
		case R.id.image_tts_set:
			Intent intent = new Intent(TtsDemo.this, TtsSettings.class);
			startActivity(intent);
			break;
		// 开始合成
		// 收到onCompleted 回调时,合成结束、生成合成音频
        // 合成的音频格式:只支持pcm格式
		case R.id.tts_play:
			String text = ((EditText) findViewById(R.id.tts_text)).getText().toString();
			// 设置参数
			setParam();
			int code = mTts.startSpeaking(text, mTtsListener);
//			/** 
//			 * 只保存音频不进行播放接口,调用此接口请注释startSpeaking接口
//			 * text:要合成的文本,uri:需要保存的音频全路径,listener:回调接口
//			*/
//			String path = Environment.getExternalStorageDirectory()+"/tts.pcm";
//			int code = mTts.synthesizeToUri(text, path, mTtsListener);
			
			if (code != ErrorCode.SUCCESS) {
				showTip("语音合成失败,错误码: " + code);	
			}
			break;
		// 取消合成
		case R.id.tts_cancel:
			mTts.stopSpeaking();
			break;
		// 暂停播放
		case R.id.tts_pause:
			mTts.pauseSpeaking();
			break;
		// 继续播放
		case R.id.tts_resume:
			mTts.resumeSpeaking();
			break;
		// 选择发音人
		case R.id.tts_btn_person_select:
			showPresonSelectDialog();
			break;
		}
	}

	
	private int selectedNum=0;
	/**
	 * 发音人选择。
	 */
	private void showPresonSelectDialog() {
		switch (mRadioGroup.getCheckedRadioButtonId()) {
		// 选择在线合成
		case R.id.tts_radioCloud:			
			new AlertDialog.Builder(this).setTitle("在线合成发音人选项")
					.setSingleChoiceItems(cloudVoicersEntries, // 单选框有几项,各是什么名字
							selectedNum, // 默认的选项
							new DialogInterface.OnClickListener() { // 点击单选框后的处理
								public void onClick(DialogInterface dialog,
										int which) { // 点击了哪一项
									voicer = cloudVoicersValue[which];
								}
							}).show();
			break;
			
		// 选择本地合成
		case R.id.tts_radioLocal:
			break;
		default:
			break;
		}
	}

	/**
	 * 初始化监听。
	 */
	private InitListener mTtsInitListener = 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 mTtsListener = 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.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 if (error != null) {
				showTip(error.getPlainDescription(true));
			}
		}

		@Override
		public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
			
		}
	};

	private void showTip(final String str) {
		mToast.setText(str);
		mToast.show();
	}

	/**
	 * 参数设置
	 * @param param
	 * @return 
	 */
	private void setParam(){
		
		// 根据合成引擎设置相应参数
		if(mEngineType.equals(SpeechConstant.TYPE_CLOUD)) {
			mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
		}else {
			mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
		}
		
		//设置发音人
		mTts.setParameter(SpeechConstant.VOICE_NAME,voicer);
		
		//设置语速
		mTts.setParameter(SpeechConstant.SPEED,mSharedPreferences.getString("speed_preference", "50"));

		//设置音调
		mTts.setParameter(SpeechConstant.PITCH,mSharedPreferences.getString("pitch_preference", "50"));

		//设置音量
		mTts.setParameter(SpeechConstant.VOLUME,mSharedPreferences.getString("volume_preference", "50"));
		
		//设置播放器音频流类型
		mTts.setParameter(SpeechConstant.STREAM_TYPE,mSharedPreferences.getString("stream_preference", "3"));
	}
	
	@Override
	protected void onDestroy() {
		super.onDestroy();
		mTts.stopSpeaking();
		// 退出时释放连接
		mTts.destroy();
	}
	
	@Override
	protected void onResume() {
		super.onResume();
	}
}

然后,我们再进行分析一下吧!

重点是点击开始合成之后的逻辑是怎么样的,是吧。

String text = ((EditText) findViewById(R.id.tts_text)).getText().toString();
			// 设置参数
			setParam();
			int code = mTts.startSpeaking(text, mTtsListener);
//			/** 
//			 * 只保存音频不进行播放接口,调用此接口请注释startSpeaking接口
//			 * text:要合成的文本,uri:需要保存的音频全路径,listener:回调接口
//			*/
//			String path = Environment.getExternalStorageDirectory()+"/tts.pcm";
//			int code = mTts.synthesizeToUri(text, path, mTtsListener);
			
			if (code != ErrorCode.SUCCESS) {
				showTip("语音合成失败,错误码: " + code);	
			}
			break;

从上面的代码以看出来,在onCreate的时候 ,创建引擎对象,然后获取到文字,其实最好还要对文字进行判断处理一下。

然后把文字扔进startSpeaking就可以了。得到一个返回值,那么,就根据这个返回值去处理结果就可以了。

非常简单,使用起来的话。

好了,这个语音的话,只分析这两部分,其他部分由于条件的限制,所以就不有啦,要是你有机会研究的话,也欢迎把你的见解分享给大家哈!

 

 

 

 

本文链接:http://blog.sunofbeaches.com/archives/707 转载请注明出处.
如果喜欢:点此订阅本站
7K
相关文章
为您推荐
各种观点

报歉!评论已关闭.