Azure AI 服务之语音识别

笔者在前文《Azure AI 服务之文本翻译》中简单介绍了 Azure 认知服务中的文本翻译 API,通过这些简单的 REST API 调用就可以轻松地进行机器翻译。如果能在程序中简单的集成语音转文本的功能会不会非常赞!本文我们就介绍如何使用必应的语音识别 API(Bing Speech API) 把语音转换成文本:

使用 Bing Speech API 可以轻松地开发出下面的应用:

你点击 "开始录音" 按钮,然后对着麦克风说话,就能够识别输出你说的内容并输出成文本。上面的截图是 Azure 官方提供的 demo,为了演示语音识别 API 的用法,我们写一个丑点的,但是可以输出详细信息的程序:

该程序会以不同的模式识别我们 hardcode 的两段音频数据,然后输出识别的结果。其中上面的文本框会输出大量的中间识别结果,而下面的文本框则输出最终的识别结果。

创建 Azure 服务

要使用 Azure 的翻译服务需要先在 Azure 上创建对应的实例,比如我们需要先创建一个 "Bing Speech API" 服务实例:

说明:对于学习和练习来说,你可以创建免费的 Azure 账号并创建免费版的上述实例,详细信息请参考 Azure 官网。

创建 WPF 程序

Bing Speech API 服务同时提供了 REST API 和客户端类库,因为 REST API 提供的服务会有一些限制,所以我们在演示程序中使用客户端类库。客户端类库分为 x86 和 x64 两个版本,笔者引用的是 x64 的版本 Microsoft.ProjectOxford.SpeechRecognition-x64:

因而需要把工程的 platform target 也设置为 x64。

需要注意的是,Azure 提供的认知服务 API 都是需要认证信息的。具体的方式就是把我们创建的服务的 key 随 API 发送的服务器端进行认证。你可以在创建的服务实例的详情界面获得对应的 key,我们在程序中通过定义的常量来保存它们:

const string SUBSCRIPTIONKEY = "your bing speech API key";

由于 demo 的代码比较长,为了能集中精力介绍 Azure AI 相关的内容,本文中只贴出相关的代码。完整的 demo 代码在这里。

识别模式

语音识别区分不同的识别模式来应对不同的使用场景,如对话模式、听写模式和交互式模式。

  • 对话模式(conversation) 在对话模式中,使用者参与的是人与人之间的对话。
  • 听写模式(dictation) 在听写模式中,使用者说出一段较长的语音然后等待语音识别的结果。
  • 交互式模式(interactive) 在交互模式中, 使用者发出简短的请求, 并期望应用程序执行响应操作。

遗憾的是在我们使用的客户端类库中,相关的模式类型并不是与上面的三种模式一一对应,类库中提供一个叫 SpeechRecognitionMode 的枚举:

public enum SpeechRecognitionMode
{
    ShortPhrase = 0,
    LongDictation = 1}

它定义了 ShortPhraseLongDictation 两种识别模式。ShortPhrase 模式最长支持 15 秒的语音。语音数据被分块发送到服务端,服务端会及时的返回部分的识别结果,所以客户端会收到多个部分结果和一个包含多个 n-best 选项的最终结果。LongDictation 模式支持最长两分钟的语音。语音数据被分块发送到服务器,根据服务端分辨出的语句间的停顿,客户端会受到多个部分结果和多个最终结果。

代码中我们要通过它们来告诉语音识别 API 执行识别的类型。比如要识别比 15s 短的语音,可以使用 ShortPhrase 模式构建 CreateDataClient 类型的实例:

// 使用工厂类型的 CreateDataClient 方法创建 DataRecognitionClient 类型的实例。this.dataClient = SpeechRecognitionServiceFactory.CreateDataClient(
    SpeechRecognitionMode.ShortPhrase ,             // 指定语音识别的模式。
    "en-US",          // 我们把语音中语言的类型 hardcode 为英语,因为我们的两个 demo 文件都是英语语音。
    SUBSCRIPTIONKEY); // Bing Speech API 服务实例的 key。

如果要识别长于 15s 的语音,就需要使用 SpeechRecognitionMode.LongDictation 模式。

分块传输音频

为了能得到近乎实时的识别效果,我们必须把音频数据以适当大小的块连续发送给服务端,下面代码中使用的块大小为 1024:

/// <summary>/// 向服务端发送语音数据。/// </summary>/// <param name="wavFileName">wav 格式文件的名称。</param>private void SendAudioHelper(string wavFileName)
{    using (FileStream fileStream = new FileStream(wavFileName, FileMode.Open, FileAccess.Read))
    {        // Note for wave files, we can just send data from the file right to the server.        // In the case you are not an audio file in wave format, and instead you have just        // raw data (for example audio coming over bluetooth), then before sending up any        // audio data, you must first send up an SpeechAudioFormat descriptor to describe        // the layout and format of your raw audio data via DataRecognitionClient's sendAudioFormat() method.
        int bytesRead = 0;        // 创建大小为 1024 的 buffer。
        byte[] buffer = new byte[1024];        try
        {            do
            {                // 把文件数据读取到 buffer 中。
                bytesRead = fileStream.Read(buffer, 0, buffer.Length);                // 通过 DataRecognitionClient 类型的实例把语音数据发送到服务端。
                this.dataClient.SendAudio(buffer, bytesRead);
            }            while (bytesRead > 0);
        }        finally
        {            // 告诉服务端语音数据已经传送完了。
            this.dataClient.EndAudio();
        }
    }
}

注意,在数据传送结束后需要通过 EndAudio() 方法显式的告诉服务端数据传送结束。

部分结果与最终结果

部分结果 把数据分块发送给语音识别服务端,我们就能得到近乎实时的识别效果。服务器端通过 OnPartialResponseReceived 事件不断把识别的结果发送到客户端。比如 demo 中演示的 ShortPhrase 模式实例,我们会得到下面的中间结果(在上面的输出框中):

--- Partial result received by OnPartialResponseReceivedHandler() ---why--- Partial result received by OnPartialResponseReceivedHandler() ---what's--- Partial result received by OnPartialResponseReceivedHandler() ---what's the weather--- Partial result received by OnPartialResponseReceivedHandler() ---what's the weather like

在识别的过程中 OnPartialResponseReceived 事件被触发了 4 次,识别的结果也越来越完整。如果应用程序能够根据这些中间结果不断地向使用者做出反馈,则应用程序就具备了实时性。

最终结果 当使用者结束语音的输入后,demo 中就是调用了 EndAudio() 函数。语音识别服务在完成识别后会触发 OnResponseReceived 事件,我们通过下面的函数把结果输出到 UI 中:

/// <summary>/// 把服务端返回的语音识别结果输出到 UI。/// </summary>/// <param name="e"><see cref="SpeechResponseEventArgs"/>该类型的实例包含语音识别的结果。</param>private void WriteResponseResult(SpeechResponseEventArgs e)
{    if (e.PhraseResponse.Results.Length == 0)
    {        this.WriteLine("No phrase response is available.");
    }    else
    {        this.WriteLine("********* Final n-BEST Results *********");        for (int i = 0; i < e.PhraseResponse.Results.Length; i++)
        {            this.WriteLine(                "[{0}] Confidence={1}, Text=\"{2}\"",
                i,
                e.PhraseResponse.Results[i].Confidence,
                e.PhraseResponse.Results[i].DisplayText);
        }        this.WriteLine();
    }
}

数据的结果大体如下:

--- OnDataShortPhraseResponseReceivedHandler ---
********* Final n-BEST Results *********[0] Confidence=High, Text="What's the weather like?"

上面是 ShortPhrase 模式的一个识别结果,它的特点是只有一个最终的返回结果,其中会包含多个识别结果并被称为 n-best。n-best 中的每个结果都包含 Confidence,DisplayText,InverseTextNormalizationResult,LexicalForm,MaskedInverseTextNormalizationResult 等属性,比如我们可以根据 Confidence 属性判断识别的结果是否可靠:

上图是实际的返回结果,因为太简单了,所以 n-best 列表中只有一条(Azure 上的语言材料,发音还是很标准的)。

对于 LongDictation 模式的识别,客户端事件 OnResponseReceived 会被触发多次,并返回分阶段的识别结果,结果中的内容和 ShortPhrase 模式类似。更详细的内容请大家直接看代码吧,很简单的。

支持语言

笔者图省事直接使用了 Azure 文档中提供的英语语音作为 demo 数据,其实 Bing Speech API 对中文支持还是比较全面的,现在支持的所有模式都支持中文。如果你还有其它需求,可以从这里查看详细的语言支持列表。

总结

笔者最早接触语音识别是在 2000 年左右,当时感觉太神奇了。只是识别的效果不太好,并且要求反复的读一个基准文档… 这么多年过去了,其实语言相关的技术发展并不算很快。 AI 的兴起让我们看到了一线希望,在介绍了 Azure AI 的语音识别服务后,让我们接着探索如何通过 AI 让程序理解文本的内容。

参考: Bing Speech Recognition API in C# for .NET

作者:sparkdev

出处:http://www.cnblogs.com/sparkdev/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

原文发布于微信公众号 - 程序你好(codinghello)

原文发表时间:2018-05-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏DeveWork

高效 Mac 人士必备:实现工作/家庭间网络环境切换的自动化

不知在看本文的你是否正处于如下情形:自带的Mac 设备(特指MacBook/Air/Pro 等苹果笔记本)在公司跟家里,因为使用的网络环境不同常常需要来回修改。...

2978
来自专栏信安之路

Red Team 工具集之信息收集

上图是一个 Red Team 攻击的生命周期,整个生命周期包括:信息收集、攻击尝试获得权限、持久性控制、权限提升、网络信息收集、横向移动、数据分析(在这个基础上...

1800
来自专栏BeJavaGod

我们是否需要升级到JDK8?

JDK8发布至今已经很久了,很多开发人员,公司都还是使用的JDK6或者JDK7,看到很多人的评论,那么我们到底要不要升级到JDK8呢? Oracle这个婊子早已...

4297
来自专栏企鹅号快讯

Python爬虫知识点梳理

学任何一门技术,都应该带着目标去学习,目标就像一座灯塔,指引你前进,很多人学着学着就学放弃了,很大部分原因是没有明确目标,所以,在你准备学爬虫前,先问问自己为什...

3007
来自专栏Java架构师学习

GIAC2017—阿里架构师眼中Dubbo的过去,现在与未来

基本介绍 Dubbo介绍 Open sourced by alibaba at 2011 Production-tested and proven AT Sca...

3847
来自专栏北京马哥教育

19个心得 明明白白说Linux下的负载均衡

一、目前网站架构一般分成负载均衡层、web层和数据库层,我其实一般还会多加一层,即文件服务器层,因为现在随着网站的PV越来越多,文件服务器的压力也越来越大;不过...

2947
来自专栏Java技术栈

Java开发必知道的国外10大网站

1、https://www.google.com/ ? 不解释 2、https://stackoverflow.com ? 里面包含各种开发遇到的问题及答案,质...

4437
来自专栏Java架构师学习

如何构建一个较为通用的业务技术架构

2155
来自专栏坚毅的PHP

HBase 异步查询导致的死锁和zookeeper通信中断问题追踪与总结[非技术]

机房T和机房Y共十台前端机,Y机房请求量是T的两倍,主要用于数据查询,开始问题是Y机房tomcat 相继僵死 1) tomcat僵死处理步骤 a 检查代码,发现...

3975
来自专栏向治洪

Android Topeka介绍

概述 当你已经做Android开发一段时间,并苦于进入瓶颈,这个时候阅读一些优秀App的源码是最好的学习进阶方式,前几天,邀请去参加一个Android大会,我作...

2358

扫码关注云+社区

领取腾讯云代金券