2015-05-28 20:01:09 liyuanjinglyj 阅读数 12764
  • Tensorflow基础入门系列

    本课程上半部分会从Tensorflow基础的框架开始介绍,从Tensorflow的安装开始,一步一步仔细讲解Tensorflow中的各种技术细节。让大家上手编写神经网络。同时也会补充一些深度学习相关的理论知识,如交叉熵,Softmax函数,深度学习中各种优化器的算法和应用等内容。 下半部分会从头开始详细讲解几个深度学习的项目,如图像识别,验证码识别,word2vec,文本分类,语音分类等。

    4391 人正在学习 去看看 覃秉丰

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


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该网址

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

                     

2019-08-29 14:45:38 a18612039484 阅读数 399
  • Tensorflow基础入门系列

    本课程上半部分会从Tensorflow基础的框架开始介绍,从Tensorflow的安装开始,一步一步仔细讲解Tensorflow中的各种技术细节。让大家上手编写神经网络。同时也会补充一些深度学习相关的理论知识,如交叉熵,Softmax函数,深度学习中各种优化器的算法和应用等内容。 下半部分会从头开始详细讲解几个深度学习的项目,如图像识别,验证码识别,word2vec,文本分类,语音分类等。

    4391 人正在学习 去看看 覃秉丰

语音识别相关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)
2018-04-24 09:02:00 dianguan5769 阅读数 92
  • Tensorflow基础入门系列

    本课程上半部分会从Tensorflow基础的框架开始介绍,从Tensorflow的安装开始,一步一步仔细讲解Tensorflow中的各种技术细节。让大家上手编写神经网络。同时也会补充一些深度学习相关的理论知识,如交叉熵,Softmax函数,深度学习中各种优化器的算法和应用等内容。 下半部分会从头开始详细讲解几个深度学习的项目,如图像识别,验证码识别,word2vec,文本分类,语音分类等。

    4391 人正在学习 去看看 覃秉丰

  来学校已经快一个月了,看语音识别依然有些吃力,HMM,GMM,DNN似懂非懂,也许多一些实践和时间,会慢慢好一些。最近终于对一个很小的知识点有一些理解,赶紧写下来,也算是一个月以来有了一些小小的成果。难免有错,热烈欢迎喷我,会及时改正!

  ---------------------------------------------------

  语音识别的模型训练当中,损失函数是一个经常出现的概念,那么什么是损失函数,是用来干什么的呢?

  理解一个东西,搞清楚脉络,对于理解是很有好处的,所以从头开始说。

  语音识别的大致过程是这样的(并不是很准确,按照个人理解)。语音信号按照音素识别,因此先对信号分帧,同时加窗处理,然后进行频域转换,再提取语音信号的特征,最后将特征输入识别模型得出结果。

  语音信号是一种短时平稳长时时变的信号,一般的认为语音信号在 20ms - 30ms 左右可以认为变化很小,这样切分出来的一小段语音,微观上变化很大,大概就可以标示一个音素,而宏观上几乎没有变化,这一过程就是分帧。我们平时录音时候看到最多的那种形式是语音信号的时域表现形式,语音识别的单位并不是一个单词,更多使用的是音素。我们可以按照这种类似于积分的思想来处理语音信号,音素在语音信号中是一段非常短的信号区域。当然分帧并不是简单的切分,为了保证信号平滑和方便处理,帧与帧之间会有重叠。同时还会对信号进行加窗处理

  时域信号的表现其实并不利于我们的处理并提取需要的信号特征,因此我们利用短时傅里叶变换,将时域信号抽取频率特征,转化成频域信号,这一个过程同时也是一个降维过程。至此,信号的特征提取变得比较容易。但是我们对语音信号的识别并不是直接识别语音信号,而是统一的提取信号里面的一部分特征,这样不仅降低维度便于处理,而且输入识别模型的数据是统一的,提取特征的方法一般是MFCC(梅尔倒谱系数),具体内容可以参见网络上大神帖子,讲的非常好,这里不深究,只需要知道这是一种提取语音信号特征的方法。至此语音信号由一串不规则的波形,变成了一个 M*N 的矩阵,表示提取了 M 种特征,语音信号被分成了 N 帧,每一帧作为矩阵的一列。

  至此语音识别的准备工作就做好了大部分,下面就开始识别模型。模型暂时可以简单看作是一个函数,把上面的 M*N 矩阵输入到模型,经过模型的处理,就能输出结果。当然一般情况下,并不是我们认为的输入语音信号就直接得出文字,语音模型的输出一般会是一个概率,模型的输入假设有 M 个节点,输出有 K 个节点,每个输出节点代表一个音素,而节点输出的值是一个概率,当概率超过一定的阈值,就认为当前节点所代表的的音素被选中。

  那么问题来了,模型是怎么来的?模型既然可以看做一个函数,那么函数里面的参数又是怎么来的?模型和模型参数,都是使用大量的训练数据训练得来的。

  那么训练之前,模型的参数是怎么来的,他又是怎么通过训练,慢慢得到一个合适的参数,用于真正的语音识别呢?这里就用到的损失函数的概念。

 

  语音识别的模型有很多,以前很多使用的隐马尔科夫模型(HMM),混合高斯模型(GMM),现在很火的深度神经网络(DNN),卷积神经网络(CNN)等。这些模型里面都有参数。其实一开始在训练模型之前,我们只是对参数进行很简单的随机初始化。作为训练数据,数据输入到模型,我们其实是预先知道应该输出什么结果的,而实际上对于一个参数是随机初始化的模型,输出结果总是不会完全正确的,那么实际数据结果和应该输出的结果之间的差距(或者说距离),就可以看做是一种度量,度量我们的初始化参数是不是合适,这种距离就可以用损失函数来表示。

  所以总结一下,损失函数并不神秘。就像我们一开始学习方程一样,一开始我们并不知道方程到底是个什么东西,后来慢慢的知道方程并不神秘,原来方程就是一个等式,只不过这个等式里面有未知数。损失函数也是这样,它就是一种度量,用于表示当前参数下的模型与我们理想的模型到底有多大的差距,以便对模型参数进行合适的调整,下一次输入训练数据,还会有一个输出,这个输出与预计结果的差距仍然用损失函数度量,依据这一次 的损失函数,再一次调整参数大小。经过很多次循环,实际输出与预计结果的差距不断缩小,我们的模型也就越来越好,直至可以用于实际,用来识别测试数据。

  

转载于:https://www.cnblogs.com/gstblog/p/8926147.html

2018-05-25 14:15:18 u012853614 阅读数 4147
  • Tensorflow基础入门系列

    本课程上半部分会从Tensorflow基础的框架开始介绍,从Tensorflow的安装开始,一步一步仔细讲解Tensorflow中的各种技术细节。让大家上手编写神经网络。同时也会补充一些深度学习相关的理论知识,如交叉熵,Softmax函数,深度学习中各种优化器的算法和应用等内容。 下半部分会从头开始详细讲解几个深度学习的项目,如图像识别,验证码识别,word2vec,文本分类,语音分类等。

    4391 人正在学习 去看看 覃秉丰

       想要灵活调用讯飞语音识别模块,要理解每个函数的功能,以及调用步骤和方式。看了两天的讯飞语音识别模块,基本理解了讯飞语音识别的工作原理。所以结合讯飞官方资料和自己的理解做一个记录,方便以后使用。

sr_init()

        讯飞语音识别给的实例demo_mic函数中,调用的第一个的函数是sr_init(),函数原型:

int sr_init(struct speech_rec * sr, const char * session_begin_params, enum sr_audsrc aud_src, int devid, struct speech_rec_notifier * notify)
{
	int errcode;
	size_t param_size;
	WAVEFORMATEX wavfmt = DEFAULT_FORMAT;

	if (aud_src == SR_MIC && get_input_dev_num() == 0) {  //get_input_dev_num 获取录音设备的总数。 
		return -E_SR_NOACTIVEDEVICE;
	}

	if (!sr)
		return -E_SR_INVAL;

	if (session_begin_params == NULL) {
		session_begin_params = DEFAULT_SESSION_PARA;
	}

	SR_MEMSET(sr, 0, sizeof(struct speech_rec));
	sr->state = SR_STATE_INIT;
	sr->aud_src = aud_src; 
	sr->ep_stat = MSP_EP_LOOKING_FOR_SPEECH;  //初始化 0 尚未开始说话
	sr->rec_stat = MSP_REC_STATUS_SUCCESS;   //识别状态  初始化部分结果成功识别
	sr->audio_status = MSP_AUDIO_SAMPLE_FIRST; //指示如何处理样本缓冲区 样本缓冲区的开始

	param_size = strlen(session_begin_params) + 1;
	sr->session_begin_params = (char*)SR_MALLOC(param_size);
	if (sr->session_begin_params == NULL) {
		sr_dbg("mem alloc failed\n");
		return -E_SR_NOMEM;
	}
	strncpy(sr->session_begin_params, session_begin_params, param_size - 1);

	sr->notif = *notify;
	
	if (aud_src == SR_MIC) {
		errcode = create_recorder(&sr->recorder, iat_cb, (void*)sr); // 创建一个录音机对象。第二个参数为回调函数(参数包括: 音频数据地址,数据长度, 在create_recorder时传入的user_cb_para用户参数。)。 创建成功后的对象在不需再使用后,使用destroy_recorder销毁.  
		if (sr->recorder == NULL || errcode != 0) {
			sr_dbg("create recorder failed: %d\n", errcode);
			errcode = -E_SR_RECORDFAIL;
			goto fail;
		}
		update_format_from_sessionparam(session_begin_params, &wavfmt);
	
		errcode = open_recorder(sr->recorder, devid, &wavfmt);  //参数 录音机对象  录音设备,windows下用整形标示,从0开始。可以使用get_default_input_dev获取默认的录音设备 录音格式
		if (errcode != 0) {
			sr_dbg("recorder open failed: %d\n", errcode);
			errcode = -E_SR_RECORDFAIL;
			goto fail;
		}
	}

	return 0;

fail:
	if (sr->recorder) {
		destroy_recorder(sr->recorder);
		sr->recorder = NULL;
	}

	if (sr->session_begin_params) {
		SR_MFREE(sr->session_begin_params);
		sr->session_begin_params = NULL;
	}
	SR_MEMSET(&sr->notif, 0, sizeof(sr->notif));

	return errcode;
}

参数一:讯飞定义的结构体,包含的录的

struct speech_rec {
	enum sr_audsrc aud_src;  //sr_audsr枚举变量,只包含两个参数SR_MIC,SR_USER, 第一个代表从麦克风数据,第二个代表从文件(这里不做记录)
	struct speech_rec_notifier notif;  /*包含三个回调函数, 初始化的三个函数分别表示:结果,开始,结束*/
	const char * session_id;  //这个参数保存的是开始一次会话是  QISRSessionBegin返回的会话ID
	int ep_stat;   //当前的指针状态,初始化是会使用一个枚举变量赋值
	int rec_stat;  //识别状态,初始化时会使用一个枚举变量赋值
	int audio_status;  //指示如何处理样本缓冲区,初始化时会使用一个枚举变量赋值
	struct recorder *recorder; //winrec.h中定义的录音机对象结构体
	volatile int state;  //状态,枚举变量只有两个值 SR_STATE_INIT  SR_STATE_STATED
	char * session_begin_params; //会话的各种参数
};

参数二:会话的各种参数,初始化是会保存到struct speech_rec定义的对象中

示例:

const char* session_begin_params = "sub = iat, domain = iat, language = zh_cn, accent = mandarin, sample_rate = 16000, result_type = plain, result_encoding = gb2312";

详细说明:点击打开链接

参数三:指示是从麦克风输入还是从用户文件输入

参数四:录音设备ID,如果为-1,则会从默认设备输入

参数五:包含三个回调函数的结构体

        调用sr_init会初始化参数sr的各种信息,并使用create_recorder打开一个录音机对象,函数执行成功后会把struct speech_rec中的recorder指向打开的录音对象,然后调用open_recorder配置录音设备和录音格式,并打开录音机

create_recorder,open_recorder说明:点击打开链接

sr_start_listening(控制流程这里不做记录)

           调用sr_init初始化信息以后,调用sr_start_listening开始进行识别,函数原型:

int sr_start_listening(struct speech_rec *sr, const char* gram)  //开始录音
{
	int ret;
	const char*		session_id = NULL;
	int				errcode = MSP_SUCCESS;

	if (sr->state >= SR_STATE_STARTED) {
		sr_dbg("already STARTED.\n");
		return -E_SR_ALREADY;
	}

	session_id = QISRSessionBegin(gram, sr->session_begin_params, &errcode); //开始一次语音识别  如果听写不需要语法,第一个参数为NULL, 如果是命令词识别,此处传入MSPUploadData的返回值
	if (MSP_SUCCESS != errcode)
	{
		sr_dbg("\nQISRSessionBegin failed! error code:%d\n", errcode);
		return errcode;
	}
	sr->session_id = session_id;
	sr->ep_stat = MSP_EP_LOOKING_FOR_SPEECH;  //有语音输入,但是没有结束
	sr->rec_stat = MSP_REC_STATUS_SUCCESS;  //成功识别一部分
	sr->audio_status = MSP_AUDIO_SAMPLE_FIRST;  //采样缓冲区的开始

	if (sr->aud_src == SR_MIC) {
		ret = start_record(sr->recorder);  //调用winrecstart_record   开始录音
		if (ret != 0) {
			sr_dbg("start record failed: %d\n", ret);
			QISRSessionEnd(session_id, "start record fail");
			sr->session_id = NULL;
			return -E_SR_RECORDFAIL;
		}
#ifdef __FILE_SAVE_VERIFY__
		open_stored_file(VERIFY_FILE_NAME);
#endif
	}

	sr->state = SR_STATE_STARTED;

	if (sr->notif.on_speech_begin)
		sr->notif.on_speech_begin();

	return 0;
}

这里面会调用QISRSessionBegin进行一次语音识别,函数调用成功返回字符串格式的sessionID,失败返回NULL。sessionID是本次识别的句柄。参数只在当次识别中生效。然后调用start_record开始录音

iat_cbsr_write_audio_data

start_record执行后,会调用在sr_init->create_recorder配置好的回调函数iat_cb,iat_cb会调用sr_write_audio_datasr_write_audio_data再调用QISRAudioWrite写入本次音频数据,然后在使用QISRGetResult获取识别结果,在使用on_result输出字符串。识别一次结束后会调用end_sr_on_vad结束一次会话

static void iat_cb(char *data, unsigned long len, void *user_para)   //创建录音机对象是传入的回调函数
{
	int errcode;
	struct speech_rec *sr;
	if (len == 0 || data == NULL)
		return;
	sr = (struct speech_rec *)user_para;
	if (sr == NULL || sr->ep_stat >= MSP_EP_AFTER_SPEECH)
		return;
#ifdef __FILE_SAVE_VERIFY__
	loopwrite_to_file(data, len);
#endif
	errcode = sr_write_audio_data(sr, data, len);  //写入录音数据
	if (errcode) {
		end_sr_on_error(sr, errcode);
		return;
	}
}
int sr_write_audio_data(struct speech_rec *sr, char *data, unsigned int len)
{
	const char *rslt = NULL;
	int ret = 0;
	if (!sr)
		return -E_SR_INVAL;
	if (!data || !len)
		return 0;
	ret = QISRAudioWrite(sr->session_id, data, len, sr->audio_status, &sr->ep_stat, &sr->rec_stat);
	if (ret) {
		end_sr_on_error(sr, ret);
		return ret;
	}
	sr->audio_status = MSP_AUDIO_SAMPLE_CONTINUE;
	if (MSP_REC_STATUS_SUCCESS == sr->rec_stat) { //已经有部分听写结果
		rslt = QISRGetResult(sr->session_id, &sr->rec_stat, 0, &ret);
		if (MSP_SUCCESS != ret)	{
			sr_dbg("\nQISRGetResult failed! error code: %d\n", ret);
			end_sr_on_error(sr, ret);
			return ret;
		}
		if (NULL != rslt && sr->notif.on_result)
			sr->notif.on_result(rslt, sr->rec_stat == MSP_REC_STATUS_COMPLETE ? 1 : 0);
	}
	if (MSP_EP_AFTER_SPEECH == sr->ep_stat)
		end_sr_on_vad(sr);
	return 0;
}

流程总结:

使用sr_init初始化相关信息---->创建(关联回调函数)并打开录音设备

sr_start_listening---->QISRSessionBegin开始一次识别---->start_record进入回调函数----->iat_cb---->sr_write_audio_data写入录音数据----->QISRAudioWrite写入本次识别的数据---->QISRGetResult识别完成获取识别结果---->on_result获取识别结果----->end_sr_on_vad一次识别完成

资料:

点击打开链接

MSC For WindowsAPI





2017-08-07 11:44:36 hzrtom 阅读数 512
  • Tensorflow基础入门系列

    本课程上半部分会从Tensorflow基础的框架开始介绍,从Tensorflow的安装开始,一步一步仔细讲解Tensorflow中的各种技术细节。让大家上手编写神经网络。同时也会补充一些深度学习相关的理论知识,如交叉熵,Softmax函数,深度学习中各种优化器的算法和应用等内容。 下半部分会从头开始详细讲解几个深度学习的项目,如图像识别,验证码识别,word2vec,文本分类,语音分类等。

    4391 人正在学习 去看看 覃秉丰

最近在尝试使用百度语音识别的API,贴出使用过程中的几个bug:

1编译环节

   我在编译sample.cpp的时候有一部分json包的函数出现undefined reference,我最后的解决方法是下了一个新的jsoncpp包,在编译时使用新的json.a链接库和原有的curl.a库,解决了这个问题。此外编译时似乎还有base64找不到,改成include base64.cpp就好了,这是一个小问题。


2运行环节

    编译成功后跑demo,发现报json param error, 3300的错误。研究后发现原因是一开始用curl获得access token的时候有一句fgets(result, MAX_BUFFER_SIZE, fpp),这里result长度是超过MAX_BUFFER_SIZE的,结果造成后面json无法解析。解决方法可以把MAX_BUFFER_SIZE宏调大(1024),或者直接在后面填json参数的时候自己跑一次curl请求然后把access token写死字符串,不过这样因为access token会过期所以会不太好。


看了一下发布日期好像是2014年,估计也没有新的维护,版本问题加上原有bug,这个示例还是有点坑的。

语音识别框架

阅读数 2979

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