我正在编写一个应用程序,它可以接受几个不同的外部输入(键盘按下、动作手势、语音)并产生类似的输出(例如,在键盘上按“T”与说出“Travel”这个词会做同样的事情大声)。因此,我不希望任何输入管理器相互了解。具体来说,我不希望 Kinect 管理器(尽可能多地)了解语音管理器,反之亦然,即使我使用的是 Kinect 的内置麦克风(语音管理器应该与任何麦克风一起使用)。我在语音管理器中使用 System.Speech,而不是 Microsoft.Speech。
我遇到了一个问题,即一旦启用 Kinect 运动识别模块,语音模块就会停止接收输入。我已经尝试了很多事情,比如反转骨架流和音频流,以不同的方式捕获音频流等等。我终于缩小了问题的范围:关于我如何初始化我的模块的一些东西不能很好地与如何我的应用程序处理事件。
该应用程序运行良好,直到动作捕捉开始。如果我完全排除 Kinect 模块,这就是我的主要方法的外观:
// Main.cs
public static void Main()
{
// Create input managers
KeyboardMouseManager keymanager = new KeyboardMouseManager();
SpeechManager speechmanager = new SpeechManager();
// Start listening for keyboard input
keymanager.start();
// Start listening for speech input
speechmanager.start()
try
{
Application.Run();
}
catch (Exception ex)
{
MessageBox.Show(ex.StackTrace);
}
}
我正在使用Application.Run()
,因为我的 GUI 是由外部程序处理的。此 C# 应用程序的唯一工作是接收输入事件并根据该输入运行外部脚本。
键盘和语音模块都偶尔接收事件。另一方面,Kinect 会不断产生事件。如果我的手势很少发生,那么轮询循环可能是每次轮询之间等待时间的答案。但是,我正在使用 Kinect 来控制鼠标移动……我不能在骨架事件捕获之间等待,因为那样鼠标会很迟钝;我的骨架捕获循环需要尽可能保持不变。这提出了一个大问题,因为现在我不能让我的 Kinect 管理器在同一个线程上(或消息泵?我对区别有点模糊,因此我认为问题出在此处):根据我的理解它,在同一个线程上不会允许键盘或语音事件始终如一地通过。反而,System.Windows.Forms
, 这样它就可以与Application.Run()
. 现在,我的主要方法如下所示:
// Main.cs
public static void Main()
{
// Create input managers
KeyboardMouseManager keymanager = new KeyboardMouseManager();
KinectManager kinectManager = new KinectManager();
SpeechManager speechmanager = new SpeechManager();
// Start listening for keyboard input
keymanager.start();
// Attempt to launch the kinect sensor
bool kinectLoaded = kinectManager.start();
// Use the default microphone (if applicable) if kinect isn't hooked up
// Use the kinect microphone array if the kinect is working
if (kinectLoaded)
{
speechmanager.start(kinectManager);
}
else
{
speechmanager.start();
}
try
{
// THIS IS THE PLACE I THINK I'M DOING SOMETHING WRONG
Application.Run(kinectManager);
}
catch (Exception ex)
{
MessageBox.Show(ex.StackTrace);
}
出于某种原因,一旦 Kinect 传感器启动,Kinect 麦克风就会失去其“默认性”(如果此观察结果不正确,或者有解决方法,请告诉我)。因此,我需要start()
在语音管理器中创建一个特殊的方法,如下所示:
// SpeechManager.cs
/** For use with the Kinect Microphone **/
public void start(KinectManager kinect)
{
// Get the speech recognizer information
RecognizerInfo recogInfo = SpeechRecognitionEngine.InstalledRecognizers().FirstOrDefault();
if (null == recogInfo)
{
Console.WriteLine("Error: No recognizer information found on Kinect");
return;
}
SpeechRecognitionEngine recognizer = new SpeechRecognitionEngine(recogInfo.Id);
// Loads all of the grammars into the recognizer engine
loadSpeechBindings(recognizer);
// Set speech event handler
recognizer.SpeechRecognized += speechRecognized;
using (var s = kinect.getAudioSource().Start() )
{
// Set the input to the Kinect audio stream
recognizer.SetInputToAudioStream(s, new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null));
// Recognize asycronous speech events
recognizer.RecognizeAsync(RecognizeMode.Multiple);
}
}
作为参考,start()
Kinect 管理器中的方法如下所示:
// KinectManager.cs
public bool start()
{
// Code from Microsoft Sample
kinect = (from sensorToCheck in KinectSensor.KinectSensors where sensorToCheck.Status == KinectStatus.Connected select sensorToCheck).FirstOrDefault();
// Fail elegantly if no kinect is detected
if (kinect == null)
{
connected = false;
Console.WriteLine("Couldn't find a Kinect");
return false;
}
// Start listening
kinect.Start();
// Enable listening for all skeletons
kinect.SkeletonStream.Enable();
// Obtain the KinectAudioSource to do audio capture
source = kinect.AudioSource;
source.EchoCancellationMode = EchoCancellationMode.None; // No AEC for this sample
source.AutomaticGainControlEnabled = false; // Important to turn this off for speech recognition
kinect.AllFramesReady += new EventHandler<AllFramesReadyEventArgs>(allFramesReady);
connected = true;
return true;
}
因此,当我禁用动作捕捉(通过让我的 main() 看起来类似于第一个代码段)时,语音识别工作正常。当我启用动作捕捉时,动作效果很好,但没有语音被识别。在这两种情况下,键盘事件总是有效的。没有错误,通过跟踪发现语音管理器中的所有数据都正确初始化了……看起来语音识别事件就消失了。如何重新组织此代码以便输入模块可以独立工作?我是否使用线程,或者只是 Application.Run() 以不同的方式?