使用Microsoft Cognitive Speech API和非麦克风实时音频流进行语音识别

时间:2018-11-08 14:26:36

标签: c# audio speech-recognition microsoft-cognitive cscore

问题

我的项目由一个桌面应用程序组成,该应用程序实时记录音频,为此,我打算从API接收实时识别反馈。使用麦克风,使用Microsoft的新语音到文本API的实时实现是微不足道的,我的情况与众不同之处仅在于我的数据已写入MemoryStream对象。

API支持

This article解释了如何使用自定义音频流实现API的Recognizerlink),这总是需要实现抽象类{{1 }}(link),以便使用PullAudioInputStream方法(link)创建所需的AudioConfig对象。换句话说,要实现我所需要的,必须实现一个回调接口。

实施尝试

由于我的数据已写入MemoryStream(并且我使用的库只会记录到文件或Stream对象),因此在下面的代码中,我只是将缓冲区复制到实现的类(),也许?)解决方法签名中的分歧。

CreatePullStream

class AudioInputCallback : PullAudioInputStreamCallback { private readonly MemoryStream memoryStream; public AudioInputCallback(MemoryStream stream) { this.memoryStream = stream; } public override int Read(byte[] dataBuffer, uint size) { return this.Read(dataBuffer, 0, dataBuffer.Length); } private int Read(byte[] buffer, int offset, int count) { return memoryStream.Read(buffer, offset, count); } public override void Close() { memoryStream.Close(); base.Close(); } } 实现如下:

Recognizer

如何将数据提供给识别器(使用CSCore):

private SpeechRecognizer CreateMicrosoftSpeechRecognizer(MemoryStream memoryStream)
{
    var recognizerConfig = SpeechConfig.FromSubscription(SubscriptionKey, @"westus");
    recognizerConfig.SpeechRecognitionLanguage =
        _programInfo.CurrentSourceCulture.TwoLetterISOLanguageName;

    // Constants are used as constructor params)
    var format = AudioStreamFormat.GetWaveFormatPCM(
        samplesPerSecond: SampleRate, bitsPerSample: BitsPerSample, channels: Channels);

    // Implementation of PullAudioInputStreamCallback
    var callback = new AudioInputCallback(memoryStream);
    AudioConfig audioConfig = AudioConfig.FromStreamInput(callback, format);

    //Actual recognizer is created with the required objects
    SpeechRecognizer recognizer = new SpeechRecognizer(recognizerConfig, audioConfig);

    // Event subscriptions. Most handlers are implemented for debugging purposes only.
    // A log window outputs the feedback from the event handlers.
    recognizer.Recognized += MsRecognizer_Recognized;
    recognizer.Recognizing += MsRecognizer_Recognizing;
    recognizer.Canceled += MsRecognizer_Canceled;
    recognizer.SpeechStartDetected += MsRecognizer_SpeechStartDetected;
    recognizer.SpeechEndDetected += MsRecognizer_SpeechEndDetected;
    recognizer.SessionStopped += MsRecognizer_SessionStopped;
    recognizer.SessionStarted += MsRecognizer_SessionStarted;

    return recognizer;
}

结果

运行该应用程序时,除了MemoryStream memoryStream = new MemoryStream(_finalSource.WaveFormat.BytesPerSecond / 2); byte[] buffer = new byte[_finalSource.WaveFormat.BytesPerSecond / 2]; _soundInSource.DataAvailable += (s, e) => { int read; _programInfo.IsDataAvailable = true; // Writes to MemoryStream as event fires while ((read = _finalSource.Read(buffer, 0, buffer.Length)) > 0) memoryStream.Write(buffer, 0, read); }; // Creates MS recognizer from MemoryStream _msRecognizer = CreateMicrosoftSpeechRecognizer(memoryStream); //Initializes loopback capture instance _soundIn.Start(); await Task.Delay(1000); // Starts recognition await _msRecognizer.StartContinuousRecognitionAsync(); SessionStarted之外,我没有得到任何异常,也没有来自API的任何响应,如下图所示。 >

enter image description here

我可以使用不同方法的建议来实现,因为我怀疑将记录的SessionStopped事件与向API实际发送数据相关联时存在一些计时问题,这使其过早地放弃了会话。没有关于为什么我的请求失败的详细反馈,我只能猜测原因。

1 个答案:

答案 0 :(得分:1)

如果没有立即可用的数据,则Read()的{​​{1}}回调应阻塞。并且PullAudioInputStream仅在流到达末尾时才返回0。然后Read()返回0(找到API参考文档here)之后,SDK将关闭流。

但是,C#MemoryStream的Read()的行为是不同的:如果缓冲区中没有可用数据,它将返回0。这就是为什么您只看到Read()SessionStart事件,而没有识别事件的原因。

为了解决此问题,您需要在SessionStopPullAudioInputStream::Read()之间添加某种同步,以确保MemoryStream::Write()将等到PullAudioInputStream::Read()写入一些数据放入缓冲区。

或者,我建议使用MemoryStream::Write(),它可以让您直接将数据写入流中。对于您的情况,在PushAudioInputStream事件中,您可以直接将数据写入_soundSource.DataAvailable而不是将数据写入MemoryStream。您可以找到PushAudioInputStream here的示例。

我们将更新文档,以提供有关如何使用推拉PushAudioInputStream的最佳实践。抱歉给您带来不便。

谢谢!