2017-07-10 10:45:10 zhonglj0314 阅读数 3374

本文是对最近学习的语音识别的一个总结,主要参考以下内容:

《解析深度学习——语音识别实践》

http://licstar.net/archives/328 词向量和语言模型

几篇论文,具体见参考文献


语音识别任务是把声音数据转换为文本,研究的主要目标是实现自然语言人机交互。在过去的几年里,语音识别领域的研究成为人们关注的热点,出现了众多新型

语音应用,例如语音搜索、虚拟语音助手(苹果的Siri)等。

语音识别任务的pipline

语音识别任务的输入是声音数据,首先要对原始的声音数据进行一系列的处理(短时傅里叶变换或取倒譜...),变成向量或矩阵的形式,称为特征序列。这个过程

是特征提取,最常用的是mfcc特征序列。这里就不深入学习了,只要知道这是语音识别的第一步。

然后,我们对这些特征序列数据建模。传统的语音识别方案中采用的是混合高斯分布建模。其实不仅仅在语音识别领域,还有很多工程和科学学科领域,高斯分

布是非常流行的。它的流行不仅来自其具有令人满意的计算特性,而且来自大数定理带来的可以近似很多自然出现的实际问题的能力。

混合高斯分布:

一个服从混合高斯分布的连续随机标量x,它的概率密度函数为:


其中          

推广到多变量的多元混合高斯分布,其联合概率密度函数为:


混合高斯分布最明显的特征是它的多模态(M>1,不同于高斯分布的单模态性质M=1。这使得混合高斯分布能够描述很多显示出多模态性质的数据(包括语音数

据),而单高斯分布则不适合。数据中的多模态性质可能来自多种潜在因素,每一个因素决定分布中一个特定的混合成分。

当语音数据经过特征提取,转换为特征序列之后,在忽略时序信息的条件下,混合高斯分布就是非常适合拟合这样的语音特征。也就是说,可以以帧为单位,用混

合高斯模型对语音特征进行建模。

如果考虑把语音顺序信息考虑进去,GMM便不再是一个好的模型,因为它不包含任何顺序信息。

这时,使用一类名叫隐马儿可夫模型(HiddenMarkov Model)来对时序信息进行建模。然而,当给定HMM的一个状态后,若要对属于该状态的语音特征向量

的概率分布进行建模,GMM仍然是一个好的模型。

所以传统的语音识别中,GMM+HMM作为声学模型。

语言模型


 语言模型其实就是看一句话是不是正常人说出来的。它可以用在很多地方,比如机器翻译、语音识别得到若干候选之后,可以利用语言模型挑一个尽量靠谱的结

果。在NLP的其它任务里也都能用到。
  语言模型形式化的描述就是给定一个字符串,看它是自然语言的概率P(w1,w2,,wt)
w1wt依次表示这句话中的各个词。有个很简单的推论是:


P(w1,w2,,wt)=P(w1)×P(w2|w1)×P(w3|w1,w2)××P(wt|w1,w2,,wt1)


  常用的语言模型都是在近似地求P(wt|w1,w2,,wt1)
。比如n-gram模型就是用P(wt|wtn+1,,wt1)近似表示前者。

语言模型经典之作:

训练语言模型的最经典之作,要数Bengio等人在2001年发表在NIPS上的文章《ANeural Probabilistic Language Model》。当然现在看的话,肯定是要看他在2003年投到JMLR上的同名论文了。

  Bengio用了一个三层的神经网络来构建语言模型,同样也是n-gram 模型。

图中最下方的wtn+1,,wt2,wt1就是前n1个词。现在需要根据这已知的n1个词预测下一个词wtC(w)表示词w所对应的词向量,整个模型中使用的是一套唯一的词向量,存在矩阵C(一个|V|×m的矩阵)中。其中|V|表示词表的大小(语料中的总词数),m表示词向量的维度。wC(w)的转化就是从矩阵中取出一行。


  网络的第一层(输入层)是将C(wtn+1),,C(wt2),C(wt1)n1个向量首尾相接拼起来,形成一个(n1)m维的向量,下面记为x

  网络的第二层(隐藏层)就如同普通的神经网络,直接使用d+Hx计算得到。d是一个偏置项。在此之后,使用tanh作为激活函数。


  网络的第三层(输出层)一共有|V|个节点,每个节点yi表示下一个词为 i的未归一化log概率。最后使用softmax激活函数将输出值y归一化成概率。最终,y

的计算公式为:

y=b+Wx+Utanh(d+Hx)

 式子中的U

一个|V|×h的矩阵)是隐藏层到输出层的参数,整个模型的多数计算集中在U和隐藏层的矩阵乘法中。后文的提到的3 个工作,都有对这一环节的简化,提升计算的速度。
  式子中还有一个矩阵W|V|×(n−1)m),这个矩阵包含了从输入层到输出层的直连边。直连边就是从输入层直接到输出层的一个线性变换,好像也是神经网络中的一种常用技巧(没有仔细考察过)。如果不需要直连边的话,将W置为0 就可以了。在最后的实验中,Bengio发现直连边虽然不能提升模型效果,但是可以少一半的迭代次数。同时他也猜想如果没有直连边,可能可以生成更好的词向量。

 现在万事俱备,用随机梯度下降法把这个模型优化出来就可以了。需要注意的是,一般神经网络的输入层只是一个输入值,而在这里,输入层x也是参数(存在C中),也是需要优化的。优化结束之后,词向量有了,语言模型也有了。

到此为止,就可以看懂语音识别的pipline


从概率角度理解语音识别

从概率角度理解语音识别任务的话,语音识别可以说是在尝试找到一个从语音到文本的最优的映射函数,使得错词率(WER)最小。

语音输入O,文本输出WW=F(O),找到F使得错词率(WER)最小


贝叶斯决策:比较不同词序列的后验概率,选择能够最大化后验概率P(W|O)的词序列作为语音识别系统的输出结果。

P(O|W),对应声学模型,用来评价语音观测样本值O和词序列W对应模型之间的匹配程度。

P(W),对应语言模型,用于表示在人类使用的自然语言中词序列W本身可能出现的概率。



声学模型的变迁

在深度学习的浪潮兴起之后DNN也应用在了语音识别领域中,主要是声学模型部分。


DNN+HMM作为声学模型


很快DNN+HMM+LM的模式被DN+LM的模式取代,利用深度神经网络实现端到端的语音识别系统。比较经典的论文:

[5] "DeepSpeech:Scaling up end-to-end speech recognition" & "Deepspeech 2: End-to-end speech recognition in english and mandarin."arXiv preprint arXiv:1512.02595 (2015). [pdf](Baidu Speech Recognition System) :star::star::star::star:

主要专注于提高嘈杂环境(例如,餐馆、汽车和公共交通)下的英语语音识别的准确率。DeepSpeech可以在嘈杂环境下实现接近80%的准确率,而其他商业版语音识别API最高识别率在65%左右。跟顶级的学术型语音识别模型(基于流行的数据集Hub500建模)相比也高出9个百分点。

Deep Speech的基础是某种递归神经网络(RNN),采用了端到端的深度学习模型。结构如图所示。


共五层,前三层是简单的DNN结构,第四层是双向RNN,第五层的输入是RNN的前向和后向单元,后面跟着softmax分类。网络输入是context特征,输出是char

训练准则是CTC,解码需要结合ngram语言模型。

DeepSpeech的成功主要得益于庞大的语音数据训练集。首先百度收集了9600个人长达7000小时语音数据,这些语音大多发生在安静的环境下。然后再将这些语音文件与包含有背景噪音的文件合成到一起,最后形成约10万小时的训练集。这些背景噪音包括了饭店、电视、自助餐厅以及汽车内、火车内等场景。另一方面,DeepSpeech的成功很大程度上要取决于规模庞大的基于GPU的深度学习基础设施。在论文中也有大量篇幅描述了优化模型训练的方法。

语音识别的最新进展:

[7]Anonline sequence-to-sequence model for noisy speech recognition

谷歌提出在线序列到序列语音识别系统。


[8]AttentionIs All You Need

attention机制.通常来说,主流序列传导模型大多基于RNNCNNGoogle此次推出的翻译框架—Transformer则完全舍弃了RNN/CNN结构,从自然语言本身的特性出发,实现了完全基于注意力机制的Transformer机器翻译网络架构。

[9]MultichannelEnd-to-end Speech Recognitio

attention-basedencoder-decoder framework 实现了cleanspeech的端到端的语音识别,本文是在其基础上进行扩展,加入了multichannelspeech enhancement,解决了噪音环境下的语音识别。

[10]OneModel To Learn Them All

[11]GAN在语音识别方面的尝试



总结和回顾:

语音识别经历了 GMM+HMM+LM ==DNN+HMM+LM ==DNN+LM的变迁。在实现深度神经网络端到端语音识别系统之后,人们在不断的尝试引入新型网络结

构、attention机制、多任务学习、GAN等技术来进一步提高语音识别特别是嘈杂环境下语音识别的准确率。

参考文献


[1]"Deep neural networks for acoustic modeling in speechrecognition: The shared views of four research groups."Hinton, Geoffrey, et al. IEEE Signal Processing Magazine 29.6 (2012):82-97. [pdf](Breakthrough in speech recognition):star::star::star::star:


深度神经网络替换传统的声学模型。


[2]"Speech recognition with deep recurrent neuralnetworks." Graves, Alex, Abdel-rahman Mohamed, andGeoffrey Hinton. 2013 IEEE international conference on acoustics,speech and signal processing. IEEE, 2013. [pdf](RNN):star::star::star:


[3] "TowardsEnd-To-End Speech Recognition with Recurrent Neural Networks."Graves, Alex, and Navdeep Jaitly. ICML. Vol. 14. 2014. [pdf]:star::star::star:

利用RNN实现端到端的语音识别。

[4] "Fastand accurate recurrent neural network acoustic models for speechrecognition."Sak, Haşim, et al. arXiv preprintarXiv:1507.06947 (2015). [pdf](Google Speech Recognition System) :star::star::star:

[5] "DeepSpeech:Scaling up end-to-end speech recognition" & "Deepspeech 2: End-to-end speech recognition in english and mandarin."arXiv preprint arXiv:1512.02595 (2015). [pdf](Baidu Speech Recognition System) :star::star::star::star:

[6] "AchievingHuman Parity in Conversational Speech Recognition." W.Xiong, J. Droppo, X. Huang, F. Seide, M. Seltzer, A. Stolcke, D. Yu,G. Zweig. arXiv preprint arXiv:1610.05256 (2016). [pdf](State-of-the-art in speech recognition, Microsoft):star::star::star::star:

[7] "Anonline sequence-to-sequence model for noisy speech recognition."

[8] "ANeural Probabilistic Language Model ."

[9]MultichannelEnd-to-end Speech Recognition

[10]OneModel To Learn Them All

[11]GAN在语音识别方面的尝试







2019-08-29 14:45:38 a18612039484 阅读数 236

语音识别相关API

语音识别关键概念:声音的本质是震动,震动的本质是位移关于时间的函数,波形文件(.wav)中记录了不同采样时刻的位移。
通过傅里叶变换,可以将时间域的声音函数分解为一系列不同频率的正弦函数的叠加,通过频率谱线的特殊分布,建立音频内容和文本的对应关系,以此作为模型训练的基础。

梅尔频率倒谱系数(mfcc)

主要思想:提取13个特征,生成梅尔频率倒谱系数矩阵。
API:

import scipy.io.wavfile as wf
import python_speech_features as sf

# 提取采样率,特征
sample_rate,sigs = wf.read('xxx.wav')
# 生成mfcc矩阵
mfcc = sf.mfcc(sigs,sample_rate)

声音合成

案例:

import json
import numpy as np
import scipy.io.wavfile as wf
# 读取存有音频信息的json文件
with open('../data/12.json', 'r') as f:
    freqs = json.loads(f.read())

tones = [
('G5', 1.5),
('A5', 0.5),
('G5', 1.5),
('E5', 0.5),
('D5', 0.5),
('E5', 0.25),
('D5', 0.25),
('C5', 0.5),
('A4', 0.5),
('C5', 0.75)]

# 设置采样率
sample_rate = 44100
# 创建一个空数组储存合成音频信息
music = np.empty(shape=1)

for tone, duration in tones:
    times = np.linspace(0, duration, duration * sample_rate)
    sound = np.sin(2 * np.pi * freqs[tone] * times)
    music = np.append(music, sound)
music *= 2 ** 15
music = music.astype(np.int16)
wf.write('music.wav', sample_rate, music)

语音识别

基本步骤: 提取声音信息(采样率,特征),生成梅尔频率倒谱系数矩阵(mfcc),训练隐马尔科夫模型,做最后的识别。
API:

import numpy as np
import scipy.io.wavfile as wf
import python_speech_features as sf
import hmmlearn.hmm as hl

# 提取样本信息
train_x,train_y = [],[]
mfccs = np.array([])
for sound_files in files_list:
    for sound_file in sound_files:
        sample_rate,sigs = wf.read(sound_file)
        mfcc = sf.mfcc(sigs,sample_rate)
        # 将mfcc矩阵添加到mfccs中
        if len(mfccs) == 0:
            mfccs == mfcc
        else:
            mfccs = np.append(mfccs,mfcc)
    # 将mfccs矩阵列表添加到训练集中  
    train_x.append(mfccs)
# 最终的train_x len(sound_files)个特征的矩阵
# train_y存的是特征标签,比如:apple,banana,pear

# 构建并训练隐马模型
models = {}
for mfccs,label in zip(train_x,train_y):
    model = hl.GaussianHMM(
        n_components = 4, covariance_type = 'diag',
        n_iter = 1000
    )
    models[label] = model.fit(mfccs)

# 同样方法获取测试集数据
# 测试
pred_y = []
for mfccs in test_x: 
    # 验证每个模型对当前mfcc的匹配度得分
    best_score, best_label = None, None
    for label, model in models.items():
        score = model.score(mfccs)
        if (best_score is None) or (best_score < score):
            best_score = score
            best_label = label
    pred_y.append(best_label)

print(test_y)
print(pred_y)
2015-05-28 20:01:09 liyuanjinglyj 阅读数 12288

语音识别主要的功能就是在用户不方便输入的时候找一个替代输入的选择。


1.本地语音识别


下面的代码首先创建SpeechRecognizer对象,并设置回调函数监听器。当在点击监听器中调用doSpeechRecognition()方法时,会使用语言参数和一个指示要在处理过程中分发部分结果的标志参数初始化语音识别。

public class MainActivity extends Activity implements View.OnClickListener{
    private Button speechBut;
    private TextView result;
    private SpeechRecognizer mSpeechRecognizer;
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.speechBut:
                doSpeechRecognition(v);
                break;
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.speechBut=(Button)findViewById(R.id.speechBut);
        this.speechBut.setOnClickListener(this);
        this.result=(TextView)findViewById(R.id.result);
        this.mSpeechRecognizer=SpeechRecognizer.createSpeechRecognizer(this);
        this.mSpeechRecognizer.setRecognitionListener(new MyRecognitionListener());
    }

    public void doSpeechRecognition(View view){
        view.setEnabled(false);
        Intent recognitionIntent=new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        recognitionIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS,true);
        recognitionIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE,"zh-CN");
        this.mSpeechRecognizer.startListening(recognitionIntent);
    }

    private class MyRecognitionListener implements RecognitionListener{
        @Override
        public void onReadyForSpeech(Bundle params) {

        }

        @Override
        public void onBeginningOfSpeech() {
            Log.i("llllllllllllllllllll","onBeginningOfSpeech");
            result.setText("");
        }

        @Override
        public void onRmsChanged(float rmsdB) {

        }

        @Override
        public void onBufferReceived(byte[] buffer) {

        }

        @Override
        public void onEndOfSpeech() {
            Log.i("llllllllllllllllllll","onEndOfSpeech");
            speechBut.setEnabled(true);
        }

        @Override
        public void onError(int error) {
            Log.i("llllllllllllllllllll","onError");
            speechBut.setEnabled(true);
        }

        @Override
        public void onResults(Bundle results) {
            Log.i("llllllllllllllllllll","onResults");
            ArrayList<String> partialResults=results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
            if(partialResults!=null && partialResults.size()>0){
                String bestResult=partialResults.get(0);
                result.setText(bestResult+".");
            }
        }

        @Override
        public void onPartialResults(Bundle bundle) {
            Log.i("llllllllllllllllllll","onPartialResults");
            ArrayList<String> partialResults=bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
            if(partialResults!=null && partialResults.size()>0){
                String bestResult=partialResults.get(0);
                result.setText(bestResult);
            }
        }

        @Override
        public void onEvent(int eventType, Bundle params) {

        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        this.mSpeechRecognizer.destroy();
    }
}

现在监听器会按照顺序接受每个方法的调用。本例在onPartialResult()方法中展现部分识别的结果,直到在onResult()方法中得到最终的结果。


一个更高级的应用程序还可以拦截单词,并监听特定的命令。这样一来,应用程序可以一直进行语音识别,直到用户明确告诉它停止-----例如,一个听写应用允许用户说“停止”单词并暂停一会后在句子间添加句号。


2.第三方语音识别(以讯飞为例)


我们大家都知道,一般Android手机是不支持中文语音识别的,我们有时候连文字转语音都要下载第三方文字转语音(TTS)API,更何况语音转文字。所以一般时候必须利用第三方提供SDK来丰富我们自己的应用功能。


就目前国内而言,在语音方面做的最好的也就属讯飞语音了。下面的开发以讯飞为例。


首先我们得到讯飞开放平台http://www.xfyun.cn/注册一个帐号。

然后如上图所示创建一个新应用,创建完应用后我们会得到如图所示的Appid:


然后下载相关的SDK包放到指定的位置,本文以Android Studio为例:

下面就可以进入开发阶段了,首先本例语音识别必须借助网络,所以本例需要的权限如下:

<!--连接网络权限,用于执行云端语音能力 -->
<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" />

本来想讲解一下具体的代码突然觉得自己的代码已经注解够详细了,并不需要一句一句的讲解。相信大家都看的懂。

public class MainActivity extends Activity {
    private static String TAG = MainActivity.class.getSimpleName();
    private Toast mToast;
    private Button showDialogs;
    private Button stop;
    private Button cancel;
    private TextView content;
    // 语音听写对象
    private SpeechRecognizer mIat;
    // 语音听写UI
    private RecognizerDialog mIatDialog;
    // 用HashMap存储听写结果
    private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();
    // 引擎类型
    private String mEngineType = SpeechConstant.TYPE_CLOUD;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.showDialogs = (Button) findViewById(R.id.showDialogs);
        this.stop = (Button) findViewById(R.id.stopL);
        this.cancel=(Button)findViewById(R.id.cancel);
        this.content = (TextView) findViewById(R.id.content);
        SpeechUtility.createUtility(this, SpeechConstant.APPID + "=5566a1f6");
        // 初始化识别无UI识别对象
        // 使用SpeechRecognizer对象,可根据回调消息自定义界面;
        mIat = SpeechRecognizer.createRecognizer(this, mInitListener);
        // 初始化听写Dialog,如果只使用有UI听写功能,无需创建SpeechRecognizer
        // 使用UI听写功能,请根据sdk文件目录下的notice.txt,放置布局文件和图片资源
        mIatDialog = new RecognizerDialog(MainActivity.this, mInitListener);
        this.showDialogs.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                content.setText("");// 清空显示内容
                mIatResults.clear();
                myRecognize();
                // 显示听写对话框
                mIatDialog.show();
                showTip(getString(R.string.text_begin));
            }
        });
        this.stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mIat.stopListening();
                showTip("停止听写");
            }
        });
        this.cancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mIat.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 void showTip(final String str) {
       this.mToast.makeText(this,str,Toast.LENGTH_SHORT).show();
    }

    private void myRecognize() {
        // 清空参数
        mIat.setParameter(SpeechConstant.PARAMS, null);
        // 设置听写引擎
        mIat.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);
        // 设置返回结果格式
        mIat.setParameter(SpeechConstant.RESULT_TYPE, "json");
        //设置引擎为转写
        mIatDialog.setParameter(SpeechConstant.DOMAIN, "iat");
        //设置识别语言为中文
        mIatDialog.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
        //设置方言为普通话
        mIatDialog.setParameter(SpeechConstant.ACCENT, "mandarin");
        //设置录音采样率为
        mIatDialog.setParameter(SpeechConstant.SAMPLE_RATE, "16000");
        //设置监听对象
        mIatDialog.setListener(recognizerDialogListener);
        // 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
        mIat.setParameter(SpeechConstant.VAD_BOS, "4000");
        // 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
        mIat.setParameter(SpeechConstant.VAD_EOS, "4000");
        // 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点
        mIat.setParameter(SpeechConstant.ASR_PTT, "1");
        // 设置听写结果是否结果动态修正,为“1”则在听写过程中动态递增地返回结果,否则只在听写结束之后返回最终结果
        // 注:该参数暂时只对在线听写有效
        mIat.setParameter(SpeechConstant.ASR_DWA, "0");
    }

    private RecognizerDialogListener recognizerDialogListener = new RecognizerDialogListener() {
        @Override
        public void onError(SpeechError error) {
            // TODO Auto-generated method stub
        }
        //当说话说完后,会回调该方法,JSON字符串在result
        @Override
        public void onResult(RecognizerResult result, boolean isLast) {
            Log.d(TAG, result.getResultString());
            printResult(result);
        }
    };

    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));
        }
        content.setText(resultBuffer.toString());
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 退出时释放连接
        mIat.cancel();
        mIat.destroy();
    }
}
当然里面有一个JSON解析,你可以自己写,也可以用SDK里面包装好的JsonParser类。

JSON解析不会的可以参考http://blog.csdn.net/liyuanjinglyj/article/details/45890825该网址

最后运行结果如下图所示:

                     

2016-12-30 15:09:26 liulanluo 阅读数 872

本文仅是自我学习,加强自己记忆。

百度语音是百度提供的语音技术服务,目前基础服务永久免费,包含语音识别、语音合成、语音唤醒三个功能。
以下先介绍语音识别功能。

按照百度语音使用流程指南(如下图),首先要注册百度账号并申请为开发者,然后在应用管理中创建应用,选择要使用的服务,这里可以选择语音识别和语音合成2种。然后下载它的SDK(含有demo——eclipse工程),最后将其libs(so库和.jar文件)集成到自己的应用中,按照开发文档开发。
这里写图片描述

下载的sdk如下图:
这里写图片描述

以下步骤都是以Android Studio的项目工程为例。

1. 将libs和res集成到自己的应用

将libs中文件和res中的文件复制到项目工程对应的文件夹,如下图
这里写图片描述

在build.gradle(Module:app)文件中添加如下代码,这是将jniLibs的路径指向libs文件夹,这样.so库就能导入项目中:

android {
   ……
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    ……
}

2.在AndroidManifest.xml文件中添加appId,权限等信息

appId等在应用管理中添加应用时会生成对应的id,key等。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.cykj.baiduyuyintest">

    <!-- 录音 -->
    <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_WIFI_STATE" />
    <!-- 获取用户手机的IMEI,用来唯一的标识用户 -->
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <!-- 写入外部存储 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    ……

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
<!-- begin: baidu speech sdk -->
        <!-- 请填写应用实际的APP_ID -->
        <meta-data
            android:name="com.baidu.speech.APP_ID"
            android:value="your appId" />
        <!-- 请填写应用实际的API_KEY -->
        <meta-data
            android:name="com.baidu.speech.API_KEY"
            android:value="your appKey" />
        <!-- 请填写应用实际的SECRET_KEY -->
        <meta-data
            android:name="com.baidu.speech.SECRET_KEY"
            android:value="your secretKey" />

        <service
            android:name="com.baidu.speech.VoiceRecognitionService"
            android:exported="false" />

        <activity
            android:name="com.baidu.voicerecognition.android.ui.BaiduASRDigitalDialog"
            android:configChanges="orientation|keyboardHidden|screenLayout"
            android:exported="false"
            android:screenOrientation="portrait"
            android:theme="@android:style/Theme.Dialog">
            <intent-filter>
                <action android:name="com.baidu.action.RECOGNIZE_SPEECH" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <!-- end : baidu speech sdk -->
    ……
   </application>

</manifest>

3.在Activity中添加代码

package com.cykj.baiduyuyintest;

import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.speech.RecognitionListener;
import android.speech.SpeechRecognizer;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.baidu.speech.VoiceRecognitionService;

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

import java.io.File;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class MainActivity extends AppCompatActivity implements RecognitionListener {


    private static final String TAG = "MainActivity";
    private SpeechRecognizer speechRecognizer;
    private TextView tvResult;
    private TextView tvError;
    private View speechTips;
    private View speechWave;

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //创建识别器
        speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this, new ComponentName(this, VoiceRecognitionService.class));
        //注册识别监听
        speechRecognizer.setRecognitionListener(this);

        //语音识别说话时显示的录音界面(麦克风)
        speechTips = View.inflate(this, R.layout.bd_asr_popup_speech, null);
        speechWave = speechTips.findViewById(R.id.wave);
        speechTips.setVisibility(View.GONE);
        addContentView(speechTips, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));

        //点击按钮开始说话进行语音识别
        Button bt = (Button) findViewById(R.id.bt);
        bt.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN://按下时开始识别
                        speechTips.setVisibility(View.VISIBLE);
                        speechRecognizer.cancel();
                        tvError.setText("");
                        tvResult.setText("");
                        startASR();
                        break;
                    case MotionEvent.ACTION_UP://抬起时停止识别
                        speechRecognizer.stopListening();
                        speechTips.setVisibility(View.GONE);
                        break;
                }
                return false;
            }
        });

        //显示识别结果
        tvResult = (TextView) findViewById(R.id.tv_result);
        //显示错误内容
        tvError = (TextView) findViewById(R.id.tv_error);

        //进入语音唤醒
        findViewById(R.id.bt_wp).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, WakeUpActivity.class));
            }
        });

    }

    /**
     * 开始识别
     */
    void startASR() {
        Intent intent = new Intent();
        //绑定识别参数
        bindParams(intent);
        speechRecognizer.startListening(intent);
    }

    /**
     * 设置识别参数
     *
     * @param intent
     */
    private void bindParams(Intent intent) {
        intent.putExtra("sample", 16000);// 离线仅支持16000采样率
        intent.putExtra("language", "cmn-Hans-CN"); // 离线仅支持中文普通话
//        intent.putExtra("prop", 10060); //垂直领域(按照文档意思应该是如果垂直领域是音乐,如果是类似发音优先识别和音乐相关的词语),2.1版本后离线功能请使用grammar参数
        //识别中的提示音,比如识别成功,识别错误等
        intent.putExtra("sound_start", R.raw.bdspeech_recognition_start);//
        intent.putExtra("sound_end", R.raw.bdspeech_speech_end);
        intent.putExtra("sound_success", R.raw.bdspeech_recognition_success);
        intent.putExtra("sound_error", R.raw.bdspeech_recognition_error);
        intent.putExtra("sound_cancel", R.raw.bdspeech_recognition_cancel);

//        intent.putExtra("vad", "input"); //语音活动检测
//        intent.putExtra("nlu", "enable"); //是否启用语义解析
        //音频源,可以识别此pcm文件中的语音内容
//        intent.putExtra("infile", Environment.getExternalStorageDirectory()+"/outfile1.pcm");
        //保存识别过程产生的录音文件
        intent.putExtra("outfile", Environment.getExternalStorageDirectory() + "/outfile.pcm");


        /**根据开发文档,这个垂类设置应该是2.1版本之前的用法,2.1版本以后都用grammar代替了。
         int prop = 10060;
         // value替换为资源文件实际路径
         intent.putExtra("asr-base-file-path", Environment.getExternalStorageDirectory()+"/s_1");

         if (prop == 10060) {
         // 地图类附加资源,value替换为资源文件实际路径
         intent.putExtra("lm-res-file-path", Environment.getExternalStorageDirectory() + "/s_2_Navi");
         } else if (prop == 20000) {
         // 语音输入附加资源,value替换为资源文件实际路径
         intent.putExtra("lm-res-file-path", Environment.getExternalStorageDirectory() + "/s_2_InputMethod");
         }*/

        // value替换为license文件实际路径,仅在使用临时license文件时需要进行设置,如果在[应用管理]中开通了离线授权,不需要设置该参数
        //具体参考http://yuyin.baidu.com/docs/asr/171中离线授权一节
//        intent.putExtra("license-file-path", Environment.getExternalStorageDirectory() + File.separator + "temp_license_2016-12-27.txt");
        //设置离线识别grammar文件,此文件来自于自定义语义设置 http://yuyin.baidu.com/asr
        intent.putExtra("grammar", "assets:///baidu_speech_grammar.bsg");
        //设置slot-data参数,可以替代grammar文件中初始词条内容,比如自定义语义设置中"name"词条内容是"name = 张三, 李四, 王五",添加slot-data后将变为"王云"、"流利"
        JSONObject slotData = new JSONObject();
        JSONArray name = new JSONArray().put("王云").put("流利");
        JSONArray app = new JSONArray().put("百度糯米").put("360卫士");
        try {
            slotData.put("name", name);
            slotData.put("appname", app);
        } catch (JSONException e) {

        }
        intent.putExtra("slot-data", slotData.toString());
        Log.d(TAG, "---" + intent.getExtras().toString());
    }


    @Override
    public void onReadyForSpeech(Bundle params) {
        // 准备就绪
        Log.v(TAG, "onReadyForSpeech--" + params);
    }


    @Override
    public void onBeginningOfSpeech() {
        // 开始说话处理
        Log.v(TAG, "onBeginningOfSpeech--");
    }


    @Override
    public void onRmsChanged(float rmsdB) {
        //音量变化处理
        final int VTAG = 0xFF00AA01;
        Integer rawHeight = (Integer) speechWave.getTag(VTAG);
        if (rawHeight == null) {
            rawHeight = speechWave.getLayoutParams().height;
            speechWave.setTag(VTAG, rawHeight);
        }

        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) speechWave.getLayoutParams();
        params.height = (int) (rawHeight * rmsdB * 0.01);
        params.height = Math.max(params.height, speechWave.getMeasuredWidth());
        speechWave.setLayoutParams(params);
    }

    @Override
    public void onBufferReceived(byte[] buffer) {
        // 录音数据传出处理
        Log.v(TAG, "onBufferReceived--");
    }

    @Override
    public void onEndOfSpeech() {
        //说话结束处理
        Log.v(TAG, "onEndOfSpeech--");
    }

    @Override
    public void onError(int error) {
        //出错
        Log.e(TAG, "onError--" + error);
        StringBuilder sb = new StringBuilder();

        switch (error) {
            case SpeechRecognizer.ERROR_AUDIO:
                sb.append("音频错误");
                break;
            case SpeechRecognizer.ERROR_CLIENT:
                sb.append("其他客户端错误");
                break;
            case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
                sb.append("权限不足");
                break;
            case SpeechRecognizer.ERROR_NETWORK:
                sb.append("网络连接错误");
                break;
            case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
                sb.append("网络连接超时");
                break;
            case SpeechRecognizer.ERROR_NO_MATCH:
                sb.append("没有匹配的识别结果");
                break;
            case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
                sb.append("引擎忙");
                break;
            case SpeechRecognizer.ERROR_SERVER:
                sb.append("服务器端错误");
                break;
            case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
                sb.append("超时");
                break;
        }

        tvError.setText(sb);

    }

    @Override
    public void onResults(Bundle results) {
        //最终结果
        Log.v(TAG, "onResults--" + results);
        tvResult.setText("");
        ArrayList<String> resultsRecognition = results.getStringArrayList("results_recognition");
        for (String s : resultsRecognition) {
            tvResult.append(s + " ");
        }
    }

    @Override
    public void onPartialResults(Bundle partialResults) {
        // 临时结果处理
        Log.v(TAG, "onPartialResults--");
    }

    @Override
    public void onEvent(int eventType, Bundle params) {
        // 处理事件回调
        Log.e(TAG, "onEvent--" + eventType + "--" + params);
    }
}

识别返回结果:

onResults--Bundle[{results_recognition=[开始], error=0, origin_result={"content":{"item":["开始"]},"result":{"sn":"d42c1a78-0f44-40e9-8ea6-80e640110a6f","idx":-11,"res_type":3,"err_no":0,"corpus_no":6369803402677818267}}}]

参考的资料:百度开发文档地址grammar文件生成地址源代码下载

2016-07-07 08:46:56 guchuanhang 阅读数 6960

高德地图官方导航部分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();
    }

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