如何在C#中使用SpInprocRecoContext识别语音事件及获取SAPI音素集
我来帮你搞定这两个和SAPI 5.4、SpInprocRecoContext相关的问题,结合你修改Windows语音词典的项目场景来一步步拆解:
SpInprocRecoContext是SAPI的进程内识别上下文,和SpSharedRecoContext的核心区别在于它不依赖系统共享的识别服务,更适合离线处理(比如你要处理WAV文件的场景)。要识别语音事件,核心步骤是初始化上下文、绑定音频源、设置识别语法、注册事件回调:
初始化Inproc识别上下文与音频输入
首先要创建SpInprocRecoContext实例,如果你是处理WAV文件,需要用SpFileStream加载音频文件并设置为上下文的音频输入流:using SpeechLib; // 创建进程内识别上下文 var recoContext = new SpInprocRecoContext(); // 加载WAV文件作为音频源(确保WAV是PCM格式,16位、16kHz单声道,符合SAPI要求) var audioStream = new SpFileStream(); audioStream.Open(@"C:\your-audio-file.wav", SpeechStreamFileMode.SSFMOpenForRead, false); recoContext.AudioInputStream = audioStream;设置识别语法并注册事件
你需要创建针对目标词汇的语法(如果是全局识别可以用系统语法,但你的场景是指定词汇,所以自定义语法更高效),然后注册Recognition事件(识别完成时触发)和Hypothesis事件(识别过程中的临时结果):// 创建自定义语法 var grammar = recoContext.CreateGrammar(1); grammar.DictationSetState(SpeechRuleState.SGDSDisabled); // 禁用听写模式 // 方式1:从XML文件加载语法 grammar.CmdLoadFromFile(@"C:\your-custom-grammar.xml", SpeechLoadOption.SLOStatic); // 方式2:直接添加单个指定词汇 // var rule = grammar.Rules.Add("TargetWord", SpeechRuleAttributes.SRATopLevel | SpeechRuleAttributes.SRADynamic, 1); // rule.InitialState.AddWordTransition(null, "你的目标词汇", "", 1, null); // grammar.Rules.Commit(); // 注册识别事件 recoContext.Recognition += new _ISpRecoContextEvents_RecognitionEventHandler(RecoContext_Recognition); recoContext.Hypothesis += new _ISpRecoContextEvents_HypothesisEventHandler(RecoContext_Hypothesis); // 启动识别 recoContext.Recognizer.SetRecoState(SpeechRecoState.SPRSActive);实现事件回调逻辑
在事件方法里处理识别结果,比如获取识别到的文本、置信度或者后续要提取的音素:private void RecoContext_Recognition(int StreamNumber, object StreamPosition, SpeechRecognitionType RecognitionType, ISpRecoResult Result) { // 获取识别到的文本 string recognizedText = Result.PhraseInfo.GetText(0, -1, true); Console.WriteLine($"识别完成:{recognizedText}"); // 音素提取逻辑会在第二部分详细说明 } private void RecoContext_Hypothesis(int StreamNumber, object StreamPosition, ISpRecoResult Result) { string tempText = Result.PhraseInfo.GetText(0, -1, true); Console.WriteLine($"临时识别结果:{tempText}"); }
你的核心需求是用SpInprocRecoContext处理WAV,然后提取指定词汇的音素。这里的关键是确保识别结果精准匹配目标词汇,然后从ISpRecoResult中提取音素信息:
构建精准匹配的自定义语法
为了避免识别歧义,最好创建只包含目标词汇的极简语法,比如:var grammar = recoContext.CreateGrammar(1); grammar.DictationSetState(SpeechRuleState.SGDSDisabled); var rule = grammar.Rules.Add("TargetVocab", SpeechRuleAttributes.SRATopLevel | SpeechRuleAttributes.SRADynamic, 1); // 添加你需要获取音素的指定词汇,比如"苹果" rule.InitialState.AddWordTransition(null, "苹果", "", 1, null); grammar.Rules.Commit(); grammar.CmdSetRuleState("TargetVocab", SpeechRuleState.SGDSActive);从识别结果中提取音素
在Recognition事件的ISpRecoResult对象中,你可以通过两种方式获取音素:private void RecoContext_Recognition(int StreamNumber, object StreamPosition, SpeechRecognitionType RecognitionType, ISpRecoResult Result) { // 方式1:直接获取词汇的音素字符串(SAPI采用美式音标变体,格式如"AA1 P AH0 L") ISpPhraseElements elements = Result.PhraseInfo.Elements; for (int i = 0; i < elements.Count; i++) { ISpPhraseElement element = elements.Item(i); string phonemes = element.Phonetic; if (!string.IsNullOrEmpty(phonemes)) { Console.WriteLine($"词汇「{element.DisplayText}」的音素集:{phonemes}"); } } // 方式2:获取带时间戳的详细音素信息 SpPhoneme[] phonemeArray; Result.GetPhonemes(out phonemeArray); foreach (var phoneme in phonemeArray) { Console.WriteLine($"音素ID:{phoneme.PhonemeID},起始位置:{phoneme.StartTime},结束位置:{phoneme.EndTime}"); } }关键注意事项
- 确保WAV文件格式符合SAPI要求:必须是PCM编码、16位深度、16kHz采样率、单声道,否则SAPI无法正确解析音频。
- 如果识别结果不准确,可以调整
AddWordTransition的第4个参数(权重值,越大优先级越高),或者提前用SpLexicon类预查词汇的标准音素做对比。 - 处理完音频后记得释放资源:
audioStream.Close(); recoContext = null;
内容的提问来源于stack exchange,提问作者Exergist

