了解如何捕获和识别长格式连续听写语音输入。
重要 API: SpeechContinuousRecognitionSession、 ContinuousRecognitionSession
在语音识别中,你学习了如何使用 SpeechRecognizer 对象的 RecognizeAsync 或 RecognizeWithUIAsync 方法捕获和识别相对短的语音输入,例如,撰写短消息服务(SMS)消息或提问时。
对于较长的连续语音识别会话(如听写或电子邮件),请使用 SpeechRecognizer 的 ContinuousRecognitionSession 属性获取 SpeechContinuousRecognitionSession 对象。
设置
你的应用需要几个对象来管理连续听写会话:
- SpeechRecognizer 对象的实例。
- 对 UI 调度程序的引用,用于在听写期间更新 UI。
- 跟踪用户说出的累积字词的方法。
在这里,我们将 SpeechRecognizer 实例声明为代码隐藏类的专用字段。 如果你希望连续听写保留到单个可扩展应用程序标记语言(XAML)页面之外,应用需要将引用存储在其他位置。
private SpeechRecognizer speechRecognizer;
听写期间,识别器从后台线程引发事件。 由于后台线程无法在 XAML 中直接更新 UI,因此应用必须使用调度程序来更新 UI 以响应识别事件。
在这里,我们声明了稍后将使用 UI 调度程序初始化的专用字段。
// Speech events may originate from a thread other than the UI thread.
// Keep track of the UI thread dispatcher so that we can update the
// UI in a thread-safe manner.
private CoreDispatcher dispatcher;
若要跟踪用户所说的内容,需要处理语音识别器引发的识别事件。 这些事件为用户话语区块提供识别结果。
在这里,我们使用 StringBuilder 对象来保存会话期间获得的所有识别结果。 新的结果在处理过程中会被追加到 StringBuilder 中。
private StringBuilder dictatedTextBuilder;
初始化
在连续语音识别的初始化过程中,必须:
- 如果在连续识别事件处理程序中更新应用的 UI,则提取 UI 线程的调度程序。
- 初始化语音识别器。
- 编译内置听写语法。 注意 语音识别至少需要一个约束来定义可识别的词汇。 如果未指定约束,则使用预定义听写语法。 请参阅 语音识别。
- 为识别事件设置事件监听器。
在此示例中,我们在 OnNavigatedTo 页面事件中初始化语音识别。
- 由于语音识别器引发的事件发生在后台线程上,因此请创建对调度程序的引用,以便更新 UI 线程。 始终在 UI 线程上调用 OnNavigatedTo。
this.dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
- 然后,初始化 SpeechRecognizer 实例。
this.speechRecognizer = new SpeechRecognizer();
然后,添加并编译用于定义 SpeechRecognizer 可识别的所有字词和短语的语法。
如果未显式指定语法,则默认使用预定义听写语法。 通常,默认语法最适合常规听写。
在这里,我们立即调用 CompileConstraintsAsync ,而无需添加语法。
SpeechRecognitionCompilationResult result =
await speechRecognizer.CompileConstraintsAsync();
处理识别事件
可以通过调用 RecognizeAsync 或 RecognizeWithUIAsync 来捕获单个简短的话语或短语。
但是,为了捕获较长的连续识别会话,我们指定在用户说话时要在后台运行的事件侦听器,并定义处理程序以生成听写字符串。
然后,我们使用识别器 的 ContinuousRecognitionSession 属性获取 SpeechContinuousRecognitionSession 对象,该对象提供用于管理连续识别会话的方法和事件。
特别是两个事件至关重要:
- ResultGenerated,当识别器生成了一些结果时发生。
- 已完成,在连续识别会话结束时发生。
当用户说话时, 将引发 ResultGenerated 事件。 识别器持续监听用户,并定期触发事件,传递一段语音输入。 必须使用事件参数的 Result 属性检查语音输入,并在事件处理程序中采取适当的作,例如将文本追加到 StringBuilder 对象。
作为 SpeechRecognitionResult 的实例, Result 属性可用于确定是否要接受语音输入。 SpeechRecognitionResult 为此提供两个属性:
下面是支持连续识别的基本步骤:
- 在这里,我们在 OnNavigatedTo 页面事件中注册 ResultGenerated 连续识别事件的处理程序。
speechRecognizer.ContinuousRecognitionSession.ResultGenerated +=
ContinuousRecognitionSession_ResultGenerated;
然后检查 Confidence 属性。 如果置信度值 为中等 或更好,我们会将文本追加到 StringBuilder。 我们还会在收集输入时更新 UI。
请注意 , ResultGenerated 事件是在无法直接更新 UI 的后台线程上引发的。 如果处理程序需要更新 UI(如 [语音和 TTS 示例]),则必须通过调度程序 RunAsync 方法将更新调度到 UI 线程。
private async void ContinuousRecognitionSession_ResultGenerated(
SpeechContinuousRecognitionSession sender,
SpeechContinuousRecognitionResultGeneratedEventArgs args)
{
if (args.Result.Confidence == SpeechRecognitionConfidence.Medium ||
args.Result.Confidence == SpeechRecognitionConfidence.High)
{
dictatedTextBuilder.Append(args.Result.Text + " ");
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
dictationTextBox.Text = dictatedTextBuilder.ToString();
btnClearText.IsEnabled = true;
});
}
else
{
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
dictationTextBox.Text = dictatedTextBuilder.ToString();
});
}
}
然后我们处理 Completed 事件,该事件表示连续听写的结束。
调用 StopAsync 或 CancelAsync 方法(描述下一部分)时,会话将结束。 会话还可以在发生错误或用户停止说话时结束。 检查事件参数的 Status 属性以确定会话结束的原因(SpeechRecognitionResultStatus)。
在这里,我们在 OnNavigatedTo 页面事件中注册已完成连续识别事件的处理程序。
speechRecognizer.ContinuousRecognitionSession.Completed +=
ContinuousRecognitionSession_Completed;
事件处理程序检查 Status 属性以确定识别是否成功。 它还处理用户停止说话的情况。 通常, TimeoutExceeded 被视为成功的识别,因为它意味着用户已完成说话。 应在代码中处理这种情况,以便获得良好的体验。
请注意 , ResultGenerated 事件是在无法直接更新 UI 的后台线程上引发的。 如果处理程序需要更新 UI(如 [语音和 TTS 示例]),则必须通过调度程序 RunAsync 方法将更新调度到 UI 线程。
private async void ContinuousRecognitionSession_Completed(
SpeechContinuousRecognitionSession sender,
SpeechContinuousRecognitionCompletedEventArgs args)
{
if (args.Status != SpeechRecognitionResultStatus.Success)
{
if (args.Status == SpeechRecognitionResultStatus.TimeoutExceeded)
{
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
rootPage.NotifyUser(
"Automatic Time Out of Dictation",
NotifyType.StatusMessage);
DictationButtonText.Text = " Continuous Recognition";
dictationTextBox.Text = dictatedTextBuilder.ToString();
});
}
else
{
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
rootPage.NotifyUser(
"Continuous Recognition Completed: " + args.Status.ToString(),
NotifyType.StatusMessage);
DictationButtonText.Text = " Continuous Recognition";
});
}
}
}
提供持续识别反馈
当人们交谈时,他们往往依靠上下文来充分理解所说的内容。 同样,语音识别器通常需要上下文来提供高置信度识别结果。 例如,“weight”和“wait”这两个词本身是不可区分的,直到可以从周围的单词中获得更多的语境。 在识别器确信单词或单词已正确识别之前,它不会引发 ResultGenerated 事件。
这可能会导致用户在继续说话时体验不够理想,因为识别器只有在具有足够的置信度来引发 ResultGenerated 事件后,才会提供结果。
处理 HypothesisGenerated 事件以改善这种表面上缺乏响应能力的情况。 每当识别器为正在处理的单词生成一组新的潜在匹配项时,将引发此事件。 事件参数提供包含当前匹配项的 假设 属性。 在用户继续说话时向用户显示这些内容,并向他们保证处理仍然处于活动状态。 一旦置信度很高并且已确定识别结果,请将临时假设结果替换为 ResultGenerated 事件中提供的最终结果。
在这里,我们将假设文本和省略号(“...”)追加到输出 TextBox 的当前值。 文本框的内容会随着生成新的假设而更新,直到从 ResultGenerated 事件获取最终结果为止。
private async void SpeechRecognizer_HypothesisGenerated(
SpeechRecognizer sender,
SpeechRecognitionHypothesisGeneratedEventArgs args)
{
string hypothesis = args.Hypothesis.Text;
string textboxContent = dictatedTextBuilder.ToString() + " " + hypothesis + " ...";
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
dictationTextBox.Text = textboxContent;
btnClearText.IsEnabled = true;
});
}
启动和停止识别
在启动识别会话之前,请检查语音识别器 State 属性的值。 语音识别器必须处于 空闲 状态。
检查语音识别器的状态后,我们通过调用语音识别器 ContinuousRecognitionSession 属性的 StartAsync 方法启动会话。
if (speechRecognizer.State == SpeechRecognizerState.Idle)
{
await speechRecognizer.ContinuousRecognitionSession.StartAsync();
}
可以通过两种方式停止识别:
- StopAsync 允许任何挂起的识别事件完成(ResultGenerated 将继续引发,直到所有挂起的识别操作完成)。
- CancelAsync 会立即终止识别会话,并放弃任何挂起的结果。
检查语音识别器的状态后,我们通过调用语音识别器 ContinuousRecognitionSession 属性的 CancelAsync 方法来停止会话。
if (speechRecognizer.State != SpeechRecognizerState.Idle)
{
await speechRecognizer.ContinuousRecognitionSession.CancelAsync();
}
注释
对 CancelAsync 的调用后,可能会发生ResultGenerated事件。
由于多线程处理,调用 CancelAsync 时,ResultGenerated 事件仍可能保留在堆栈上。 如果是这样,则ResultGenerated事件仍会触发。
如果在取消识别会话时设置任何专用字段,请始终在 ResultGenerated 处理程序中确认其值。 在取消会话时,如果将一个字段设置为 null,请不要认为在处理程序中该字段已被初始化。
相关文章
示例