2018-03-15 16:55:57 u012375207 阅读数 460
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5921 人正在学习 去看看 杨波

写在前面

时隔一年没有写博客了,最近的一篇博客是2017年4月17日,怎么说呢,一个字,懒。哈哈,当然实际上是从去年四月开始接到做语音识别的项目去了,一直到现在也没什么空(其实有空,但是还是懒)。被各种bug折磨了一整年之后,终于也算是熬出头了,这里就写一个关于语音框架构建的博客,不过因为公司项目,肯定不能说得太详细了,说个大概吧,具体代码之类的可以去看看百度的那个duros,虽然我觉得它写得很烂。

关于语音识别

语音识别是一个很庞大的东西,当然对于我们这样的客户端选手来说,需要了解的也不多,这里简单的说一下,因为很庞大,所以篇幅原因,我就不说太详细(其实还是因为懒)。

云端识别

语音识别因为需要很大的数据量作为支持,所以要得到很好的体验,只能通过服务器跑神经网络来实现,所以大部分的识别是放在云端的,这里本地只需要做好网络和录放音即可。然后语音识别分两种,一种叫做听写机,还有一种叫做JSGF。他们两个的区别就在于听写机是你随便说什么,他都能给你识别出来,对错就不知道,也没有一个固定的值,JSGF就只能够识别固定的词语,你设置了他就能识别,没有设置他就不能识别。这里你就可以看看Siri的实现,当你对Siri说打电话给张三的时候,“打电话给”这四个字就是云端听写机实现的识别,“张三”这两个字就是JSGF识别的,因为张三在当前情况下一定是一个人名,那么它就会拿这个语音到你的通讯录,通过JSGF来识别。JSGF简单的说就是在一个固定的结果集里面,一定会识别出一个结果给你。如果这里我没解释清楚的,自己去百度看看,记住,就是两种东西:听写机和JSGF。

本地识别

由于刚才说了识别就分两种,一种是听写机,还有一种是JSGF,因为听写机需要很庞大的数据支撑,所以本地只能使用JSGF,当然这也至少有二三十兆。JSGF放在本地一般用作唤醒,比如类似hey Siri这种操作,当然Siri还做了声纹识别。还用作识别第一个、第二个这种类似选择的命令词。

端点检测

这个用作检测当前是否有人说话。

回声消除

回声消除主要用在当设备在播放音频的同时还要录音识别的时候,因为这种时候mic会把播放出来的音频一起录制回去,所以需要做一个消除的算法,由于android手机的AudioTrack和AudioRecord中都存在一个环形缓冲区,而每一个厂家的缓冲区大小不同,所以会造成录制的音频和当前播放的音频不一致,音频起点无法对齐,而出现消除效果很差的情况,所以这个功能在Android手机上是一个难以突破的瓶颈,不过可以勉强使用系统自带的communication来实现,不过很多厂家可能是由于技术原因吧,这个communication的参数根本没用,这里点名小米,小米垃圾,哈哈哈。

TTS

TTS就是把文字生成为音频的一个东西,也分为云端和本地,当然是云端效果好,不过本地也有它的作用,比如说没有网络的时候。

SILK

这是一个音频压缩库,主要用于本地和服务器通信的时候对数据做压缩处理。

正文开始咯

思路

怎么说呢,这个东西是一个很庞大的,一时半儿说不清楚,这里涉及太多东西了,我先挨个说怎么做,至于为什么这样做,能解释的就解释,不能解释的,就先挨个去了解这些知识,最后就知道为什么了。

分包

一共分4个包:

  1. SILK音频压缩
  2. TTS+音频播放
  3. 本地识别+端点检测+回声消除+音频录制
  4. 云端识别

本地识别包的实现

一个录音数据同步队列
一个识别结果同步队列
一个录音线程
一个识别线程
内部流程:录音线程录制音频放入录音队列,识别线程从录音线程取出数据识别后放入结果队列
外部调用:从结果队列取出结果进行操作
结果数据包括:识别内容、音频原始数据、端点数据

TTS包的实现

一个生成同步队列
一个结果同步队列
一个生成线程
一个播放线程
内部流程:生成线程从生成队列中取出文字进行生成,生成的音频放进结果队列,播放线程从结果队列中取出音频进行播放
外部调用:需要传入interface从内部回调播放状态

云端识别的实现

云端识别需要依赖本地识别库,然后从本地识别库中取数据往云端发,这里需要一个网络队列即可。

具体业务实现

架构

采用MVP架构,能够做到多个子流程解耦,比如说在Siri中,播放音乐和拨打电话,这能够分为两个子流程,他们和主流程(对话流程)是毫不相干的,但是他们却是操作的同一个页面,那么这里我们能够联想到MVP架构,我只需要在切换流程的时候切换不同的presenter即可,页面不用进行其他操作,而且每个presenter中的业务也毫不相干,可以多人共同协助开发。这里的业务分发根据云端识别后进行语义分析返回的业务标签来判断,这里我们可以考虑做成注解的方式,子流程在类名上注解自己处理的业务标签,主流程在得到业务标签后进行遍历,反射调用子流程,这样的话完完全全能够模仿Spring的操作了。

然后我感觉好像也没什么可说的了,因为涉及到太多不能说的东西,而且还有很多单独拿出来都很庞大的东西,不是三言两语能说清楚的,比如说JSGF,这里只是提供一个思路,提供一个方向,按照这个放下往下走,是可以构建出一个语音识别的开发框架的。你们可以找一下各种语音开放平台,下载这些库,来根据我这个大概的思路进行实现。

2016-11-25 21:32:40 wanwanchengxuyuan 阅读数 791
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5921 人正在学习 去看看 杨波

Hololens上语音输入有三种形式,分别是:
- 语音命令 Voice Command
- 听写 Diction
- 语法识别 Grammar Recognizer

HoloLens开发手记 - 语音识别(语音命令) 博客已经介绍了 Voice Command 的用法。本文将介绍听写的用法:

听写识别 Diction


听写就是语音转化成文字 (Speech to Text)。此特性在HoloLens上使用的场所一般多用于需要用到键入文字的地方,例如在HoloLens中使用 Edge 搜索时,由于在HoloLens上一般是非常规的物理键盘输入,使用手势点按虚拟键盘键入文字的具体操作需要用户转动头部将Gaze射线光标定位到想输入的虚拟键盘字母上,再用Gesture点按手势确认选定此字母,由此可见还是有极大的不便性。

Paste_Image.png

所以语音转为文字实现键入内容的操作将能大大提高效率。

听写特性用于将用户语音转为文字输入,同时支持内容推断和事件注册特性。Start()和Stop()方法用于启用和禁用听写功能,在听写结束后需要调用Dispose()方法来关闭听写页面。GC会自动回收它的资源,如果不Dispose会带来额外的性能开销。

使用听写识别应该注意的是:
1. 在你的应用中必须打开 Microphone  特性。设置如下:Edit -> Project Settings -> Player -> Windows Store -> Publishing Settings > Capabilities 中确认勾上Microphone。
2. 必须确认HoloLens连接上了wifi,这样听写识别才能工作。

DictationRecognizer.cs

using HoloToolkit;
using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Windows.Speech;

public class MicrophoneManager : MonoBehaviour
{
    [Tooltip("A text area for the recognizer to display the recognized strings.")]
    public Text DictationDisplay;

    private DictationRecognizer dictationRecognizer;

    // Use this string to cache the text currently displayed in the text box.
    //使用此字符串可以缓存当前显示在文本框中的文本。
    private StringBuilder textSoFar;

    void Awake()
    {
        /* TODO: DEVELOPER CODING EXERCISE 3.a */

        //Create a new DictationRecognizer and assign it to dictationRecognizer variable.
        dictationRecognizer = new DictationRecognizer();

        //Register for dictationRecognizer.DictationHypothesis and implement DictationHypothesis below
        // This event is fired while the user is talking. As the recognizer listens, it provides text of what it's heard so far.
        //注册听写假设事件。此事件在用户说话时触发。当识别器收听时,提供到目前为止所听到的内容文本
        dictationRecognizer.DictationHypothesis += DictationRecognizer_DictationHypothesis;

        //Register for dictationRecognizer.DictationResult and implement DictationResult below
        // This event is fired after the user pauses, typically at the end of a sentence. The full recognized string is returned here.
        //注册听写结果事件。此事件在用户暂停后触发,通常在句子的结尾处,返回完整的已识别字符串
        dictationRecognizer.DictationResult += DictationRecognizer_DictationResult;

        //Register for dictationRecognizer.DictationComplete and implement DictationComplete below
        // This event is fired when the recognizer stops, whether from Stop() being called, a timeout occurring, or some other error.
        //注册听写完成事件。无论是调用Stop()函数、发生超时或者其他的错误使得识别器停止都会触发此事件
        dictationRecognizer.DictationComplete += DictationRecognizer_DictationComplete;

        //Register for dictationRecognizer.DictationError and implement DictationError below
        // This event is fired when an error occurs.
        //注册听写错误事件。当发生错误时调用此事件,通常是为连接网络或者在识别过程中网络发生中断等时产生错误
        dictationRecognizer.DictationError += DictationRecognizer_DictationError;

        // Shutdown the PhraseRecognitionSystem. This controls the KeywordRecognizers
        //PhraseRecognitionSystem控制的是KeywordRecognizers,关闭语音命令关键字识别。只有在关闭这个后才能开启听写识别
        PhraseRecognitionSystem.Shutdown();

        //Start dictationRecognizer
        //开启听写识别
        dictationRecognizer.Start();

    }

    /// <summary>
    /// This event is fired while the user is talking. As the recognizer listens, it provides text of what it's heard so far.
    /// </summary>
    /// <param name="text">The currently hypothesized recognition.</param>
    private void DictationRecognizer_DictationHypothesis(string text)
    {
        // Set DictationDisplay text to be textSoFar and new hypothesized text
        // We don't want to append to textSoFar yet, because the hypothesis may have changed on the next event
        DictationDisplay.text = textSoFar.ToString() + " " + text + "...";
    }

    /// <summary>
    /// This event is fired after the user pauses, typically at the end of a sentence. The full recognized string is returned here.
    /// </summary>
    /// <param name="text">The text that was heard by the recognizer.</param>
    /// <param name="confidence">A representation of how confident (rejected, low, medium, high) the recognizer is of this recognition.</param>
    private void DictationRecognizer_DictationResult(string text, ConfidenceLevel confidence)
    {
        // 3.a: Append textSoFar with latest text
        textSoFar.Append(text + "");

        // 3.a: Set DictationDisplay text to be textSoFar
        DictationDisplay.text = textSoFar.ToString();
    }

    /// <summary>
    /// This event is fired when the recognizer stops, whether from Stop() being called, a timeout occurring, or some other error.
    /// Typically, this will simply return "Complete". In this case, we check to see if the recognizer timed out.
    /// </summary>
    /// <param name="cause">An enumerated reason for the session completing.</param>
    private void DictationRecognizer_DictationComplete(DictationCompletionCause cause)
    {
        // If Timeout occurs, the user has been silent for too long.
        // With dictation, the default timeout after a recognition is 20 seconds.
        // The default timeout with initial silence is 5 seconds.
        //如果在听写开始后第一个5秒内没听到任何声音,将会超时  
        //如果识别到了一个结果但是之后20秒没听到任何声音,也会超时  
        if (cause == DictationCompletionCause.TimeoutExceeded)
        {
            Microphone.End(deviceName);

            DictationDisplay.text = "Dictation has timed out. Please press the record button again.";
            SendMessage("ResetAfterTimeout");
        }
    }

    /// <summary>
    /// This event is fired when an error occurs.
    /// </summary>
    /// <param name="error">The string representation of the error reason.</param>
    /// <param name="hresult">The int representation of the hresult.</param>
    private void DictationRecognizer_DictationError(string error, int hresult)
    {
        // 3.a: Set DictationDisplay text to be the error string
        DictationDisplay.text = error + "\nHRESULT: " + hresult;
    }


    // Update is called once per frame  
    void Update () {  

    }  

    void OnDestroy()  
    {  
        dictationRecognizer.Stop();  
        dictationRecognizer.DictationHypothesis -= DictationRecognizer_DictationHypothesis;  
        dictationRecognizer.DictationResult -= DictationRecognizer_DictationResult;  
        dictationRecognizer.DictationComplete -= DictationRecognizer_DictationComplete;  
        dictationRecognizer.DictationError -= DictationRecognizer_DictationError;  
        dictationRecognizer.Dispose();  
    }  

}

HoloLens只能运行单个语音识别 (run at a time),所以若要使用听写识别的话,必须要关闭KeywordRecognizer

DictationRecognizer中设置有两个超时
1. 如果识别器启用并且在5秒内没有听到任何声音,将会超时。
2. 如果识别器识别到了结果,但是在20秒内没有听到声音,将会超时。

2014-03-31 00:45:29 PullThin_Youth 阅读数 3330
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5921 人正在学习 去看看 杨波

      接触android几个月以来第一次写博客,有点小激动,相信这一举动会慢慢见证我在IT路上的成长;由于最近项目中用到了语音识别技术,乘着周末有时间记录下来,本人新手一枚,高手勿喷 ^_^


我这里用的是科大讯飞语音API,具体步骤如下:

1、首先进入科大讯飞语音云平台http://open.voicecloud.cn/,注册一个开发者账号

2、创建新应用,得到一个app_id号,然后下载android平台SDK

3、开始创建我们自己的应用,项目中需要在lib文件夹下加入Msc.jar和libmsc.so两个文件,注意libmsc.so是在armeabi文件夹下


布局界面和运行界面



在工程AndroidManifest.xml 文件中添加如下权限

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

布局文件代码

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.xj.audiodemo.MainActivity"
    android:orientation="vertical"
    tools:ignore="MergeRootFrame" >
    
    <TextView 
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/white"
        android:background="@drawable/titlebar_bg"
        android:text="@string/audio_write"
        android:textSize="25dp"
        />
    
    <EditText 
        android:id="@+id/et_content"
        android:layout_marginTop="10dp"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_width="match_parent"
        android:gravity="top"
        android:layout_height="180dp"
        android:background="@drawable/logo"
        />
    <Button
       	android:id="@+id/bt_start"
        android:layout_marginTop="10dp" 
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/start_audio"
        android:background="@drawable/btn_effect"
        />
	
</LinearLayout>

MainActivity代码

package com.xj.audiodemo;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.EditText;
import android.widget.Toast;

import com.iflytek.cloud.speech.RecognizerResult;
import com.iflytek.cloud.speech.SpeechConstant;
import com.iflytek.cloud.speech.SpeechError;
import com.iflytek.cloud.speech.SpeechListener;
import com.iflytek.cloud.speech.SpeechUser;
import com.iflytek.cloud.ui.RecognizerDialog;
import com.iflytek.cloud.ui.RecognizerDialogListener;
import com.xj.audiodemo.util.JsonParser;

public class MainActivity extends ActionBarActivity implements OnClickListener {
	private EditText editText;
	//听写dialog
	private RecognizerDialog recognizerDialog;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.activity_main);
		
		loginAudio();
		init();
	}

	private void loginAudio() {
		SpeechUser.getUser().login(this, null, null, "appid="+getString(R.string.app_id), listener);
	}

	private void init() {
		editText = (EditText) findViewById(R.id.et_content);
		findViewById(R.id.bt_start).setOnClickListener(this);
		
		//创建听写dialog
		recognizerDialog = new RecognizerDialog(this);
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.bt_start://开始录音
			showAduioDialog();
			break;
		}
	}
	
	/**
	 * 显示听写dialog
	 */
	private void showAduioDialog(){
		if(null != recognizerDialog){
			recognizerDialog = new RecognizerDialog(this);
		}
		//清空Grammar_ID,防止识别后进行听写时Grammar_ID的干扰
		recognizerDialog.setParameter(SpeechConstant.CLOUD_GRAMMAR, null);
		//设置听写dialog的引擎
		recognizerDialog.setParameter(SpeechConstant.DOMAIN, getString(R.string.engine));
		//设置采样率参数  支持8k 和 16k
		recognizerDialog.setParameter(SpeechConstant.SAMPLE_RATE, "8000");
		//清空显示框
		editText.setText(null);
		//显示听写对话框
		recognizerDialog.setListener(dialogListener);
		recognizerDialog.show();
	}
	
	private RecognizerDialogListener dialogListener = new RecognizerDialogListener() {
		
		//识别返回结果
		@Override 
		public void onResult(RecognizerResult results, boolean isLast) {
			//results返回的是json串 需要解析
			String text = JsonParser.parseIatResult(results.getResultString());
			editText.append(text);
			editText.setSelection(editText.length());
		}
		
		//识别回调错误
		@Override 
		public void onError(SpeechError error) {
			
		}
	};
	
	/**
	 * 用户的登陆的回调函数
	 */
	private SpeechListener listener = new SpeechListener() {
		
		@Override
		public void onEvent(int arg0, Bundle arg1) {}
		
		@Override
		public void onData(byte[] arg0) {}
		
		//登陆完成返回的结果
		@Override
		public void onCompleted(SpeechError error) {
			if(null != error){
				Toast.makeText(MainActivity.this, getString(R.string.login_error), Toast.LENGTH_SHORT).show();
			}else{
				Toast.makeText(MainActivity.this, getString(R.string.login_success), Toast.LENGTH_SHORT).show();
			}
		}
	};
	
	@Override
	protected void onStop() {
		super.onStop();
		if(null != recognizerDialog){
			recognizerDialog.cancel();
		}
	}

}


Util工具类 JSON解析

package com.xj.audiodemo.util;

import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;

import android.text.TextUtils;

/**
 * @author x j
 */
public class JsonParser {
	
	/**
	 * 听写结果的Json格式解析
	 * @param json
	 * @return string
	 */
	public static String parseIatResult(String json) {
		if(TextUtils.isEmpty(json))
			return "";
		
		StringBuffer ret = new StringBuffer();
		try {
			JSONTokener tokener = new JSONTokener(json);
			JSONObject joResult = new JSONObject(tokener);

			JSONArray words = joResult.getJSONArray("ws");
			for (int i = 0; i < words.length(); i++) {
				JSONArray items = words.getJSONObject(i).getJSONArray("cw");// 听写结果词,默认使用第一个结果
				JSONObject obj = items.getJSONObject(0);
				ret.append(obj.getString("w"));
			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return ret.toString();
	}
}

本程序员自己开发的两款小应用,欢迎大家试玩 

《史上最牛的手相大师》 在安卓市场,91,百度手机助手,360手机助手,豌豆荚,应用宝已上线

豌豆荚 http://www.wandoujia.com/apps/com.example.handmoney

安卓市场 http://apk.hiapk.com/appinfo/com.example.handmoney

百度手机助手 http://as.baidu.com/a/item?docid=6800908&pre=web_am_se

《史上最牛的人脸评分仪》 在 在安卓市场,91,百度手机助手,360手机助手,豌豆荚已上线

豌豆荚 http://www.wandoujia.com/apps/com.xj.facerecognition

安卓市场 http://apk.hiapk.com/appinfo/com.xj.facerecognition

百度手机助手 http://as.baidu.com/a/item?docid=6802582&pre=web_am_se









                                
2016-07-07 08:46:56 guchuanhang 阅读数 6967
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5921 人正在学习 去看看 杨波

高德地图官方导航部分Demo使用android sdk 24编译,执行会崩溃。经过和高德工程师的交涉得知,地图中使用了科大讯飞的语音模块,崩溃的就是语音部分。探究一下科大讯飞语音识别,在此记录一下成长点滴!
这里使用的是科大讯飞

科大讯飞API提供的功能

  • 语音合成

    将一段文字转换为成语音,可根据需要合成出不同音色、语速和语调的声音,让机器像人一样开口说话。

  • 语音听写

    将一段语音转换成文本,把语音中包含文字信息提取出来,并可以优先识别用户手机特有的联系人和个性化数据。

  • 语法识别

    判断用户所说的内容是否与预定义的语法相符合, 主要用于识别用户是否下达某项指令,使用语法识别前,需要先定义语法。

  • 语义理解

    在语音听写基础上,分析理解用户的说话意图,返回结构化的指令信息。开发者可在语义开放平台定义专属的问答格式。

  • 语音评测

    通过智能语音技术自动对发音水平进行评价,给出用户综合得分和发音信息。

  • 声纹密码

    据语音波形反映说话人生理和行为特征的语音参数,自动识别说话人身份,声纹识别所提供的安全性可与其他生物识别技术(指纹、掌形和虹膜)相媲美。

  • 人脸识别

    基于人的脸部特征信息进行身份识别的一种生物识别技术,可以自动在图像中检测和跟踪人脸,进而对检测到的人脸进行检测和验证。系统同时支持人脸关键点检出、视频流人脸检测等功能,识别率高达 99%。

由于时间和能力限制,这里仅仅对语音听写&&语音合成中的云服务部分进行简单探究

环境搭建

讯飞官网,申请一个开发者账号,创建一个应用,下载相应的sdk,添加相关的jar和so文件。
1.在Application中完成讯飞模块的初始化

 @Override
    public void onCreate() {
        super.onCreate();
        // 将“12345678”替换成您申请的 APPID
// 请勿在“ =” 与 appid 之间添加任务空字符或者转义符
SpeechUtility.createUtility(context, SpeechConstant.APPID +"=12345678");
        SpeechUtility.createUtility(this, SpeechConstant.APPID +"=12345678");
    }

2.添加相应的权限

  <!--连接网络权限,用于执行云端语音能力 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!--获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 -->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <!--读取网络信息状态 -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!--获取当前wifi状态 -->
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <!--允许程序改变网络连接状态 -->
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <!--读取手机信息权限 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <!--读取联系人权限,上传联系人需要用到此权限 -->
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <!--外存储写权限, 构建语法需要用到此权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!--外存储读权限,构建语法需要用到此权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <!--配置权限,用来记录应用配置信息 -->
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />

    <!--摄相头权限, 拍照需要用到 -->
    <uses-permission android:name="android.permission.CAMERA" />

语音合成

就是将文本读出来
使用的布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
    <!--activity_text2speech.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:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingLeft="10dp"
    android:paddingRight="10dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="讯飞合成示例"
        android:textSize="30sp" />

    <EditText
        android:id="@+id/tts_text"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="top|left"
        android:text="科大讯飞作为中国最大的智能语音技术提供商,在智能语音技术领域有着长期的研究积累,并在中文语音合成、语音识别、
    口语评测等多项技术上拥有国际领先的成果。科大讯飞是我国唯一以语音技术为产业化方向的“国家863计划成果产业化基地”…"
        android:textSize="20sp" />


    <Button
        android:id="@+id/tts_play"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="开始合成"
        android:textSize="20sp" />

</LinearLayout>

java代码如下:

package com.iflytek.voicedemo;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Environment;
import android.support.v4.app.ActivityCompat;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.EditText;
import android.widget.Toast;

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

public class Text2SpeechActivity extends Activity implements OnClickListener {
    // 语音合成对象
    private SpeechSynthesizer mTts;
    private Toast mToast;

    @SuppressLint("ShowToast")
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_text2speech);

        findViewById(R.id.tts_play).setOnClickListener(this);
        // 初始化合成对象
        mTts = SpeechSynthesizer.createSynthesizer(this, null);

        mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
        applyPermission();
    }

    //获取权限,将语音保存到sd卡
    private void applyPermission() {
        ActivityCompat.requestPermissions(this, new String[]{
                Manifest.permission.READ_EXTERNAL_STORAGE
        }, 12);

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            // 开始合成
            // 收到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;
        }

    }

    /**
     * 合成回调监听。
     */
    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) {
            // 合成进度
        }

        @Override
        public void onSpeakProgress(int percent, int beginPos, int endPos) {
            // 播放进度
        }

        @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) {
        mToast.setText(str);
        mToast.show();
    }

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

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

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

}

使用官方封装的UI,进行语音听写

为了便于开发者调用,讯飞封装了一套用于语音输入的UI。在使用之前需要将官方Demo中的assets目录下的iflytek放到你的项目的assets目录下。其内容如下:

这里写图片描述
展示界面的布局文件

<?xml version="1.0" encoding="utf-8"?>
    <!--activity_speech2text_ui.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:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingLeft="10dp"
    android:paddingRight="10dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="讯飞听写示例"
        android:textSize="30sp" />

    <EditText
        android:id="@+id/iat_text"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="top|left"
        android:paddingBottom="20dp"
        android:textSize="20sp" />

    <Button
        android:id="@+id/iat_recognize"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="开始"
        android:textSize="20sp" />

</LinearLayout>

java代码:

package com.iflytek.voicedemo;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.EditText;
import android.widget.Toast;

import com.iflytek.cloud.RecognizerResult;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.ui.RecognizerDialog;
import com.iflytek.cloud.ui.RecognizerDialogListener;
import com.iflytek.speech.util.JsonParser;

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

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

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

    private EditText mResultText;
    private Toast mToast;

    @SuppressLint("ShowToast")
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_speech2text_ui);

        findViewById(R.id.iat_recognize).setOnClickListener(this);
        // 初始化听写Dialog,如果只使用有UI听写功能,无需创建SpeechRecognizer
        // 使用UI听写功能,放置布局文件和图片资源
        //使用云服务InitListener参数写成null
        mIatDialog = new RecognizerDialog(this, null);

        mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
        mResultText = ((EditText) findViewById(R.id.iat_text));
        applyPermission();
    }

    private void applyPermission() {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 12);

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            // 开始听写
            // 如何判断一次听写结束:OnResult isLast=true 或者 onError
            case R.id.iat_recognize:
                mResultText.setText(null);// 清空显示内容
                mIatResults.clear();
                // 显示听写对话框
                mIatDialog.setListener(mRecognizerDialogListener);
                mIatDialog.show();
                showTip("请开始说话…");
                break;
            default:
                break;
        }
    }


    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 mRecognizerDialogListener = 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) {
        mToast.setText(str);
        mToast.show();
    }

}

解释服务器返回数据,用到的工具类

package com.iflytek.speech.util;

import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONTokener;

import android.util.Log;

/**
 * Json结果解析类
 */
public class JsonParser {

    public static String parseIatResult(String json) {
        StringBuffer ret = new StringBuffer();
        try {
            JSONObject joResult = new JSONObject(json);

            JSONArray words = joResult.getJSONArray("ws");
            for (int i = 0; i < words.length(); i++) {
                // 转写结果词,默认使用第一个结果
                JSONArray items = words.getJSONObject(i).getJSONArray("cw");
                JSONObject obj = items.getJSONObject(0);
                ret.append(obj.getString("w"));
//              如果需要多候选结果,解析数组其他字段
//              for(int j = 0; j < items.length(); j++)
//              {
//                  JSONObject obj = items.getJSONObject(j);
//                  ret.append(obj.getString("w"));
//              }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } 
        return ret.toString();
    }

    public static String parseGrammarResult(String json) {
        StringBuffer ret = new StringBuffer();
        try {
            JSONTokener tokener = new JSONTokener(json);
            JSONObject joResult = new JSONObject(tokener);

            JSONArray words = joResult.getJSONArray("ws");
            for (int i = 0; i < words.length(); i++) {
                JSONArray items = words.getJSONObject(i).getJSONArray("cw");
                for(int j = 0; j < items.length(); j++)
                {
                    JSONObject obj = items.getJSONObject(j);
                    if(obj.getString("w").contains("nomatch"))
                    {
                        ret.append("没有匹配结果.");
                        return ret.toString();
                    }
                    ret.append("【结果】" + obj.getString("w"));
                    ret.append("【置信度】" + obj.getInt("sc"));
                    ret.append("\n");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            ret.append("没有匹配结果.");
        } 
        return ret.toString();
    }

    public static String parseLocalGrammarResult(String json) {
        StringBuffer ret = new StringBuffer();
        try {
            JSONTokener tokener = new JSONTokener(json);
            JSONObject joResult = new JSONObject(tokener);

            JSONArray words = joResult.getJSONArray("ws");
            for (int i = 0; i < words.length(); i++) {
                JSONArray items = words.getJSONObject(i).getJSONArray("cw");
                for(int j = 0; j < items.length(); j++)
                {
                    JSONObject obj = items.getJSONObject(j);
                    if(obj.getString("w").contains("nomatch"))
                    {
                        ret.append("没有匹配结果.");
                        return ret.toString();
                    }
                    ret.append("【结果】" + obj.getString("w"));
                    ret.append("\n");
                }
            }
            ret.append("【置信度】" + joResult.optInt("sc"));

        } catch (Exception e) {
            e.printStackTrace();
            ret.append("没有匹配结果.");
        } 
        return ret.toString();
    }
}

这里写图片描述
官方定制的UI还是挺漂亮的!

不使用官方UI控件进行语音听写

使用到的布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
    <!--activity_speech2text.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:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingLeft="10dp"
    android:paddingRight="10dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="讯飞听写示例"
        android:textSize="30sp" />

    <EditText
        android:id="@+id/iat_text"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:gravity="top|left"
        android:paddingBottom="20dp"
        android:textSize="20sp" />


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="2dp"
        android:layout_marginTop="10dp"
        android:gravity="center_horizontal"
        android:orientation="horizontal">

        <Button
            android:id="@+id/iat_recognize"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="开始"
            android:textSize="20sp" />

        <Button
            android:id="@+id/iat_stop"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="停止"
            android:textSize="20sp" />

        <Button
            android:id="@+id/iat_cancel"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="取消"
            android:textSize="20sp" />
    </LinearLayout>

</LinearLayout>

java代码:

package com.iflytek.voicedemo;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.EditText;
import android.widget.Toast;

import com.iflytek.cloud.ErrorCode;
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.speech.util.JsonParser;

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

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

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

    private EditText mResultText;
    private Toast mToast;

    @SuppressLint("ShowToast")
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_speech2text);

        findViewById(R.id.iat_recognize).setOnClickListener(Speech2TextActivity.this);
        findViewById(R.id.iat_stop).setOnClickListener(Speech2TextActivity.this);
        findViewById(R.id.iat_cancel).setOnClickListener(Speech2TextActivity.this);
        mIat = SpeechRecognizer.createRecognizer(this, null);

        mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
        mResultText = ((EditText) findViewById(R.id.iat_text));
        applyPermission();
    }

    private void applyPermission() {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 12);

    }

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

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            // 开始听写
            // 如何判断一次听写结束:OnResult isLast=true 或者 onError
            case R.id.iat_recognize:
                mResultText.setText(null);// 清空显示内容
                mIatResults.clear();
                // 设置参数
                setParam();
                ret = mIat.startListening(mRecognizerListener);
                if (ret != ErrorCode.SUCCESS) {
                    showTip("听写失败,错误码:" + ret);
                }
                break;
            case R.id.iat_stop:
                mIat.stopListening();
                showTip("停止听写");
                break;
            case R.id.iat_cancel:
                mIat.cancel();
                showTip("取消听写");
                break;
            default:
                break;
        }
    }


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

        @Override
        public void onBeginOfSpeech() {
            // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
            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);
        }

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


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

    /**
     * 参数设置
     */
    public void setParam() {
        // 清空参数
        mIat.setParameter(SpeechConstant.PARAMS, null);
        // 设置听写引擎
        mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
        // 设置返回结果格式
        mIat.setParameter(SpeechConstant.RESULT_TYPE, "json");

        // 设置语言
        mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
        // 设置语言区域
        mIat.setParameter(SpeechConstant.ACCENT, "mandarin");

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

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

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

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

}
2016-04-10 11:39:55 qq_31810357 阅读数 14882
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5921 人正在学习 去看看 杨波


原文地址http://blog.csdn.net/qq_31810357/article/details/51111702

前言:最近研究了一下语音识别,从百度语音识别到讯飞语音识别;首先说一下个人针对两者的看法,讯飞毫无疑问比较专业,识别率也很高真对语音识别是比较精准的,但是很多开发者和我一样期望离线识别,而讯飞离线是收费的;请求次数来讲,两者都可以申请高配额,针对用户较多的几乎都一样。基于免费并且支持离线我选择了百度离线语音识别。比较简单,UI设计多一点,下面写一下教程:

1.首先:需要的库

2.我是自定义的UI所以以功能实现为主(头文件)

// 头文件
#import "BDVRCustomRecognitonViewController.h"
#import "BDVRClientUIManager.h"
#import "WBVoiceRecordHUD.h"
#import "BDVRViewController.h"
#import "MyViewController.h"
#import "BDVRSConfig.h"

3.需要知道的功能:能用到的如下:

//-------------------类方法------------------------
// 创建语音识别客户对像,该对像是个单例
+ (BDVoiceRecognitionClient *)sharedInstance;

// 释放语音识别客户端对像
+ (void)releaseInstance;


//-------------------识别方法-----------------------
// 判断是否可以录音
- (BOOL)isCanRecorder;

// 开始语音识别,需要实现MVoiceRecognitionClientDelegate代理方法,并传入实现对像监听事件
// 返回值参考 TVoiceRecognitionStartWorkResult
- (int)startVoiceRecognition:(id<MVoiceRecognitionClientDelegate>)aDelegate;

// 说完了,用户主动完成录音时调用
- (void)speakFinish;

// 结束本次语音识别
- (void)stopVoiceRecognition;

/**
 * @brief 获取当前识别的采样率
 *
 * @return 采样率(16000/8000)
 */
- (int)getCurrentSampleRate;

/**
 * @brief 得到当前识别模式(deprecated)
 *
 * @return 当前识别模式
 */
- (int)getCurrentVoiceRecognitionMode __attribute__((deprecated));

/**
 * @brief 设置当前识别模式(deprecated),请使用-(void)setProperty:(TBDVoiceRecognitionProperty)property;
 *
 * @param 识别模式
 *
 * @return 是否设置成功
 */
- (void)setCurrentVoiceRecognitionMode:(int)aMode __attribute__((deprecated));

// 设置识别类型
- (void)setProperty:(TBDVoiceRecognitionProperty)property __attribute__((deprecated));

// 获取当前识别类型
- (int)getRecognitionProperty __attribute__((deprecated));

// 设置识别类型列表, 除EVoiceRecognitionPropertyInput和EVoiceRecognitionPropertySong外
// 可以识别类型复合
- (void)setPropertyList: (NSArray*)prop_list;

// cityID仅对EVoiceRecognitionPropertyMap识别类型有效
- (void)setCityID: (NSInteger)cityID;

// 获取当前识别类型列表
- (NSArray*)getRecognitionPropertyList;

//-------------------提示音-----------------------
// 播放提示音,默认为播放,录音开始,录音结束提示音
// BDVoiceRecognitionClientResources/Tone
// record_start.caf   录音开始声音文件
// record_end.caf     录音结束声音文件
// 声音资源需要加到项目工程里,用户可替换资源文件,文件名不可以变,建音提示音不宜过长,0。5秒左右。
// aTone 取值参考 TVoiceRecognitionPlayTones,如没有找到文件,则返回NO
- (BOOL)setPlayTone:(int)aTone isPlay:(BOOL)aIsPlay;

4.录音按钮相关动画(我自定义的,大家可以借鉴)


// 录音按钮相关
@property (nonatomic, weak, readonly) UIButton *holdDownButton;// 说话按钮
/**
 *  是否取消錄音
 */
@property (nonatomic, assign, readwrite) BOOL isCancelled;

/**
 *  是否正在錄音
 */
@property (nonatomic, assign, readwrite) BOOL isRecording;
/**
 *  当录音按钮被按下所触发的事件,这时候是开始录音
 */
- (void)holdDownButtonTouchDown;

/**
 *  当手指在录音按钮范围之外离开屏幕所触发的事件,这时候是取消录音
 */
- (void)holdDownButtonTouchUpOutside;

/**
 *  当手指在录音按钮范围之内离开屏幕所触发的事件,这时候是完成录音
 */
- (void)holdDownButtonTouchUpInside;

/**
 *  当手指滑动到录音按钮的范围之外所触发的事件
 */
- (void)holdDownDragOutside;

5.初始化系统UI

#pragma mark - layout subViews UI

/**
 *  根据正常显示和高亮状态创建一个按钮对象
 *
 *  @param image   正常显示图
 *  @param hlImage 高亮显示图
 *
 *  @return 返回按钮对象
 */
- (UIButton *)createButtonWithImage:(UIImage *)image HLImage:(UIImage *)hlImage ;
- (void)holdDownDragInside;
- (void)createInitView; // 创建初始化界面,播放提示音时会用到
- (void)createRecordView;  // 创建录音界面
- (void)createRecognitionView; // 创建识别界面
- (void)createErrorViewWithErrorType:(int)aStatus; // 在识别view中显示详细错误信息
- (void)createRunLogWithStatus:(int)aStatus; // 在状态view中显示详细状态信息

- (void)finishRecord:(id)sender; // 用户点击完成动作
- (void)cancel:(id)sender; // 用户点击取消动作

- (void)startVoiceLevelMeterTimer;
- (void)freeVoiceLevelMeterTimerTimer;

6.最重要的部分


// 录音完成
 [[BDVoiceRecognitionClient sharedInstance] speakFinish];

// 取消录音
[[BDVoiceRecognitionClient sharedInstance] stopVoiceRecognition];

7.两个代理方法

- (void)VoiceRecognitionClientWorkStatus:(int)aStatus obj:(id)aObj
{
    switch (aStatus)
    {
        case EVoiceRecognitionClientWorkStatusFlushData: // 连续上屏中间结果
        {
            NSString *text = [aObj objectAtIndex:0];
            
            if ([text length] > 0)
            {
//                [clientSampleViewController logOutToContinusManualResut:text];
                
                UILabel *clientWorkStatusFlushLabel = [[UILabel alloc]initWithFrame:CGRectMake(kScreenWidth/2 - 100,64,200,60)];
                clientWorkStatusFlushLabel.text = text;
                clientWorkStatusFlushLabel.textAlignment = NSTextAlignmentCenter;
                clientWorkStatusFlushLabel.font = [UIFont systemFontOfSize:18.0f];
                clientWorkStatusFlushLabel.numberOfLines = 0;
                clientWorkStatusFlushLabel.backgroundColor = [UIColor whiteColor];
                [self.view addSubview:clientWorkStatusFlushLabel];
                
            }

            break;
        }
        case EVoiceRecognitionClientWorkStatusFinish: // 识别正常完成并获得结果
        {
			[self createRunLogWithStatus:aStatus];
            
            if ([[BDVoiceRecognitionClient sharedInstance] getRecognitionProperty] != EVoiceRecognitionPropertyInput)
            {
                //  搜索模式下的结果为数组,示例为
                // ["公园", "公元"]
                NSMutableArray *audioResultData = (NSMutableArray *)aObj;
                NSMutableString *tmpString = [[NSMutableString alloc] initWithString:@""];
                
                for (int i=0; i < [audioResultData count]; i++)
                {
                    [tmpString appendFormat:@"%@\r\n",[audioResultData objectAtIndex:i]];
                }
                
                clientSampleViewController.resultView.text = nil;
                [clientSampleViewController logOutToManualResut:tmpString];
                
            }
            else
            {
                NSString *tmpString = [[BDVRSConfig sharedInstance] composeInputModeResult:aObj];
                [clientSampleViewController logOutToContinusManualResut:tmpString];
                
            }
           
            if (self.view.superview)
            {
                [self.view removeFromSuperview];
            }
            
            break;
        }
        case EVoiceRecognitionClientWorkStatusReceiveData:
        {
            // 此状态只有在输入模式下使用
            // 输入模式下的结果为带置信度的结果,示例如下:
            //  [
            //      [
            //         {
            //             "百度" = "0.6055192947387695";
            //         },
            //         {
            //             "摆渡" = "0.3625582158565521";
            //         },
            //      ]
            //      [
            //         {
            //             "一下" = "0.7665404081344604";
            //         }
            //      ],
            //   ]
//暂时关掉 -- 否则影响跳转结果
//            NSString *tmpString = [[BDVRSConfig sharedInstance] composeInputModeResult:aObj];
//            [clientSampleViewController logOutToContinusManualResut:tmpString];
            
            break;
        }
        case EVoiceRecognitionClientWorkStatusEnd: // 用户说话完成,等待服务器返回识别结果
        {
			[self createRunLogWithStatus:aStatus];
            if ([BDVRSConfig sharedInstance].voiceLevelMeter)
            {
                [self freeVoiceLevelMeterTimerTimer];
            }
			
            [self createRecognitionView];
            
            break;
        }
        case EVoiceRecognitionClientWorkStatusCancel:
        {            
            if ([BDVRSConfig sharedInstance].voiceLevelMeter) 
            {
                [self freeVoiceLevelMeterTimerTimer];
            }
            
			[self createRunLogWithStatus:aStatus];  
            
            if (self.view.superview) 
            {
                [self.view removeFromSuperview];
            }
            break;
        }
        case EVoiceRecognitionClientWorkStatusStartWorkIng: // 识别库开始识别工作,用户可以说话
        {
            if ([BDVRSConfig sharedInstance].playStartMusicSwitch) // 如果播放了提示音,此时再给用户提示可以说话
            {
                [self createRecordView];
            }
            
            if ([BDVRSConfig sharedInstance].voiceLevelMeter)  // 开启语音音量监听
            {
                [self startVoiceLevelMeterTimer];
            }
            
			[self createRunLogWithStatus:aStatus]; 

            break;
        }
		case EVoiceRecognitionClientWorkStatusNone:
		case EVoiceRecognitionClientWorkPlayStartTone:
		case EVoiceRecognitionClientWorkPlayStartToneFinish:
		case EVoiceRecognitionClientWorkStatusStart:
		case EVoiceRecognitionClientWorkPlayEndToneFinish:
		case EVoiceRecognitionClientWorkPlayEndTone:
		{
			[self createRunLogWithStatus:aStatus];
			break;
		}
        case EVoiceRecognitionClientWorkStatusNewRecordData:
        {
            break;
        }
        default:
        {
			[self createRunLogWithStatus:aStatus];
            if ([BDVRSConfig sharedInstance].voiceLevelMeter) 
            {
                [self freeVoiceLevelMeterTimerTimer];
            }
            if (self.view.superview) 
            {
                [self.view removeFromSuperview];
            }
 
            break;
        }
    }
}

- (void)VoiceRecognitionClientNetWorkStatus:(int) aStatus
{
    switch (aStatus) 
    {
        case EVoiceRecognitionClientNetWorkStatusStart:
        {	
			[self createRunLogWithStatus:aStatus];
            [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
            break;   
        }
        case EVoiceRecognitionClientNetWorkStatusEnd:
        {
			[self createRunLogWithStatus:aStatus];
			[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
            break;   
        }          
    }
}

8.录音按钮的一些操作


#pragma mark ------ 关于按钮操作的一些事情-------
- (void)holdDownButtonTouchDown {
    // 开始动画
    _disPlayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(delayAnimation)];
    _disPlayLink.frameInterval = 40;
    [_disPlayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    
    self.isCancelled = NO;
    self.isRecording = NO;
    
 // 开始语音识别功能,之前必须实现MVoiceRecognitionClientDelegate协议中的VoiceRecognitionClientWorkStatus:obj方法
    int startStatus = -1;
    startStatus = [[BDVoiceRecognitionClient sharedInstance] startVoiceRecognition:self];
    if (startStatus != EVoiceRecognitionStartWorking) // 创建失败则报告错误
    {
        NSString *statusString = [NSString stringWithFormat:@"%d",startStatus];
        [self performSelector:@selector(firstStartError:) withObject:statusString afterDelay:0.3];  // 延迟0.3秒,以便能在出错时正常删除view
        return;
    }
    // "按住说话-松开搜索"提示
    [voiceImageStr removeFromSuperview];
    voiceImageStr = [[UIImageView alloc]initWithFrame:CGRectMake(kScreenWidth/2 - 40, kScreenHeight - 153, 80, 33)];
    voiceImageStr.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"searchVoice"]];
    [self.view addSubview:voiceImageStr];
   
}

- (void)holdDownButtonTouchUpOutside {
    // 结束动画
    [self.view.layer removeAllAnimations];
    [_disPlayLink invalidate];
    _disPlayLink = nil;
    
    // 取消录音
    [[BDVoiceRecognitionClient sharedInstance] stopVoiceRecognition];
    
    if (self.view.superview)
    {
        [self.view removeFromSuperview];
    }
}

- (void)holdDownButtonTouchUpInside {
    // 结束动画
    [self.view.layer removeAllAnimations];
    [_disPlayLink invalidate];
    _disPlayLink = nil;
    
    [[BDVoiceRecognitionClient sharedInstance] speakFinish];
}

- (void)holdDownDragOutside {
    
    //如果已經開始錄音了, 才需要做拖曳出去的動作, 否則只要切換 isCancelled, 不讓錄音開始.
    if (self.isRecording) {
//        if ([self.delegate respondsToSelector:@selector(didDragOutsideAction)]) {
//            [self.delegate didDragOutsideAction];
//        }
    } else {
        self.isCancelled = YES;
    }
}


#pragma mark - layout subViews UI

- (UIButton *)createButtonWithImage:(UIImage *)image HLImage:(UIImage *)hlImage {
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(kScreenWidth/2 -36, kScreenHeight - 120, 72, 72)];
    
    if (image)
        [button setBackgroundImage:image forState:UIControlStateNormal];
    if (hlImage)
        [button setBackgroundImage:hlImage forState:UIControlStateHighlighted];
    
    return button;
}

#pragma mark ----------- 动画部分 -----------
- (void)startAnimation
{
    CALayer *layer = [[CALayer alloc] init];
    layer.cornerRadius = [UIScreen mainScreen].bounds.size.width/2;
    layer.frame = CGRectMake(0, 0, layer.cornerRadius * 2, layer.cornerRadius * 2);
    layer.position = CGPointMake([UIScreen mainScreen].bounds.size.width/2,[UIScreen mainScreen].bounds.size.height - 84);
    //    self.view.layer.position;
    UIColor *color = [UIColor colorWithRed:arc4random()%10*0.1 green:arc4random()%10*0.1 blue:arc4random()%10*0.1 alpha:1];
    layer.backgroundColor = color.CGColor;
    [self.view.layer addSublayer:layer];
    
    CAMediaTimingFunction *defaultCurve = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault];
    
    _animaTionGroup = [CAAnimationGroup animation];
    _animaTionGroup.delegate = self;
    _animaTionGroup.duration = 2;
    _animaTionGroup.removedOnCompletion = YES;
    _animaTionGroup.timingFunction = defaultCurve;
    
    CABasicAnimation *scaleAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale.xy"];
    scaleAnimation.fromValue = @0.0;
    scaleAnimation.toValue = @1.0;
    scaleAnimation.duration = 2;
    
    CAKeyframeAnimation *opencityAnimation = [CAKeyframeAnimation animationWithKeyPath:@"opacity"];
    opencityAnimation.duration = 2;
    opencityAnimation.values = @[@0.8,@0.4,@0];
    opencityAnimation.keyTimes = @[@0,@0.5,@1];
    opencityAnimation.removedOnCompletion = YES;
    
    NSArray *animations = @[scaleAnimation,opencityAnimation];
    _animaTionGroup.animations = animations;
    [layer addAnimation:_animaTionGroup forKey:nil];
    
    [self performSelector:@selector(removeLayer:) withObject:layer afterDelay:1.5];
}

- (void)removeLayer:(CALayer *)layer
{
    [layer removeFromSuperlayer];
}


- (void)delayAnimation
{
    [self startAnimation];
}

完成以上操作,就大功告成了!

温馨提示:

1.由于是语音识别,需要用到麦克风相关权限,模拟器会爆12个错误,使用真机可以解决;

2.涉及到授权文件相关并不复杂,工程Bundle Identifier只需要设置百度的离线授权一致即可,如下图:


最终效果如下:



有不懂或不明白的地方可以微博联系我:


iOS开发者交流群:446310206





没有更多推荐了,返回首页