我的项目由一个桌面应用程序组成,该应用程序实时记录音频,为此,我打算从API接收实时识别反馈。使用麦克风,使用Microsoft的新语音到文本API的实时实现是微不足道的,我的情况与众不同之处仅在于我的数据已写入MemoryStream
对象。
This article解释了如何使用自定义音频流实现API的Recognizer
(link),这总是需要实现抽象类{{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的任何响应,如下图所示。 >
我可以使用不同方法的建议来实现,因为我怀疑将记录的SessionStopped
事件与向API实际发送数据相关联时存在一些计时问题,这使其过早地放弃了会话。没有关于为什么我的请求失败的详细反馈,我只能猜测原因。
答案 0 :(得分:1)
如果没有立即可用的数据,则Read()
的{{1}}回调应阻塞。并且PullAudioInputStream
仅在流到达末尾时才返回0。然后Read()
返回0(找到API参考文档here)之后,SDK将关闭流。
但是,C#MemoryStream的Read()的行为是不同的:如果缓冲区中没有可用数据,它将返回0。这就是为什么您只看到Read()
和SessionStart
事件,而没有识别事件的原因。
为了解决此问题,您需要在SessionStop
和PullAudioInputStream::Read()
之间添加某种同步,以确保MemoryStream::Write()
将等到PullAudioInputStream::Read()
写入一些数据放入缓冲区。
或者,我建议使用MemoryStream::Write()
,它可以让您直接将数据写入流中。对于您的情况,在PushAudioInputStream
事件中,您可以直接将数据写入_soundSource.DataAvailable
而不是将数据写入MemoryStream
。您可以找到PushAudioInputStream
here的示例。
我们将更新文档,以提供有关如何使用推拉PushAudioInputStream
的最佳实践。抱歉给您带来不便。
谢谢!