【问题标题】:Streaming input to System.Speech.Recognition.SpeechRecognitionEngine将输入流式传输到 System.Speech.Recognition.SpeechRecognitionEngine
【发布时间】:2010-12-13 13:33:28
【问题描述】:

我正在尝试通过 TCP 套接字在 C# 中进行“流式”语音识别。我遇到的问题是 SpeechRecognitionEngine.SetInputToAudioStream() 似乎需要一个可以寻找的定义长度的流。现在我能想到的唯一方法是随着更多输入的进入,在 MemoryStream 上重复运行识别器。

这里有一些代码来说明:

            SpeechRecognitionEngine appRecognizer = new SpeechRecognitionEngine();

            System.Speech.AudioFormat.SpeechAudioFormatInfo formatInfo = new System.Speech.AudioFormat.SpeechAudioFormatInfo(8000, System.Speech.AudioFormat.AudioBitsPerSample.Sixteen, System.Speech.AudioFormat.AudioChannel.Mono);

            NetworkStream stream = new NetworkStream(socket,true);
            appRecognizer.SetInputToAudioStream(stream, formatInfo);
            // At the line above a "NotSupportedException" complaining that "This stream does not support seek operations."

有谁知道如何解决这个问题?它必须支持某种类型的流式输入,因为它可以很好地与使用 SetInputToDefaultAudioDevice() 的麦克风配合使用。

谢谢,肖恩

【问题讨论】:

  • 也许SetInputToDefaultAudioDevice() 是微软的“黑魔法”(常见),或者它执行某种批处理,如您所建议的。

标签: c# sockets streaming speech-recognition sapi


【解决方案1】:

这是我的解决方案。

class FakeStreamer : Stream
{
    public bool bExit = false;
    Stream stream;
    TcpClient client;
    public FakeStreamer(TcpClient client)
    {
        this.client = client;
        this.stream = client.GetStream();
        this.stream.ReadTimeout = 100; //100ms
    }
    public override bool CanRead
    {
        get { return stream.CanRead; }
    }

    public override bool CanSeek
    {
        get { return false; }
    }

    public override bool CanWrite
    {
        get { return stream.CanWrite; }
    }

    public override long Length
    {
        get { return -1L; }
    }

    public override long Position
    {
        get { return 0L; }
        set { }
    }
    public override long Seek(long offset, SeekOrigin origin)
    {
        return 0L;
    }

    public override void SetLength(long value)
    {
        stream.SetLength(value);
    }
    public override int Read(byte[] buffer, int offset, int count)
    {
        int len = 0, c = count;
        while (c > 0 && !bExit)
        {
            try
            {
                len = stream.Read(buffer, offset, c);
            }
            catch (Exception e)
            {
                if (e.HResult == -2146232800) // Timeout
                {
                    continue;
                }
                else
                {
                    //Exit read loop
                    break;
                }
            }
            if (!client.Connected || len == 0)
            {
                //Exit read loop
                return 0;
            }
            offset += len;
            c -= len;
        }
        return count;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        stream.Write(buffer,offset,count);
    }

    public override void Close()
    {
        stream.Close();
        base.Close();
    }

    public override void Flush()
    {
        stream.Flush();
    }
}

如何使用:

//client connect in
TcpClient clientSocket = ServerSocket.AcceptTcpClient();
FakeStreamer buffStream = new FakeStreamer(clientSocket);
...
//recognizer init
m_recognizer.SetInputToAudioStream(buffStream , audioFormat);
...
//recognizer end
if (buffStream != null)
    buffStream.bExit = true;

【讨论】:

    【解决方案2】:

    我通过覆盖流类使实时语音识别工作:

    class SpeechStreamer : Stream
    {
        private AutoResetEvent _writeEvent;
        private List<byte> _buffer;
        private int _buffersize;
        private int _readposition;
        private int _writeposition;
        private bool _reset;
    
        public SpeechStreamer(int bufferSize)
        {
            _writeEvent = new AutoResetEvent(false);
             _buffersize = bufferSize;
             _buffer = new List<byte>(_buffersize);
             for (int i = 0; i < _buffersize;i++ )
                 _buffer.Add(new byte());
            _readposition = 0;
            _writeposition = 0;
        }
    
        public override bool CanRead
        {
            get { return true; }
        }
    
        public override bool CanSeek
        {
            get { return false; }
        }
    
        public override bool CanWrite
        {
            get { return true; }
        }
    
        public override long Length
        {
            get { return -1L; }
        }
    
        public override long Position
        {
            get { return 0L; }
            set {  }
        }
    
        public override long Seek(long offset, SeekOrigin origin)
        {
            return 0L;
        }
    
        public override void SetLength(long value)
        {
    
        }
    
        public override int Read(byte[] buffer, int offset, int count)
        {
            int i = 0;
            while (i<count && _writeEvent!=null)
            {
                if (!_reset && _readposition >= _writeposition)
                {
                    _writeEvent.WaitOne(100, true);
                    continue;
                }
                buffer[i] = _buffer[_readposition+offset];
                _readposition++;
                if (_readposition == _buffersize)
                {
                    _readposition = 0;
                    _reset = false;
                }
                i++;
            }
    
            return count;
        }
    
        public override void Write(byte[] buffer, int offset, int count)
        {
            for (int i = offset; i < offset+count; i++)
            {
                _buffer[_writeposition] = buffer[i];
                _writeposition++;
                if (_writeposition == _buffersize)
                {
                    _writeposition = 0;
                    _reset = true;
                }
            }
            _writeEvent.Set();
    
        }
    
        public override void Close()
        {
            _writeEvent.Close();
            _writeEvent = null;
            base.Close();
        }
    
        public override void Flush()
        {
    
        }
    }
    

    ... 并将其实例用作 SetInputToAudioStream 方法的流输入。一旦流返回长度或返回的计数小于请求的计数,识别引擎就认为输入已经完成。这会设置一个永远不会结束的循环缓冲区。

    【讨论】:

    • 嗨,肖恩,我一直在努力让您的解决方案发挥作用,但到目前为止还没有成功。与上面的其他所有内容一样,从磁盘文件中可以正常工作,但不适用于 MemoryStream。您是否偶尔发出识别请求,或者您是否能够使用 SpeechHypothesized、SpeechRecognized 事件?您可以发布更多代码来提供帮助吗?谢谢!
    • 对不起,错过了你的问题,你去吧。有了它,我就可以进行实时语音识别,还可以通过网络传输音频(我的开源项目 ispy 的一部分 - ispyconnect.com
    • 感谢 Sean...很棒的项目。
    • 你是个天才肖恩!您的代码的最新版本完美运行!从 Skype 捕获输出并针对 SAPI 运行以进行语音识别。非常感谢您的帮助....
    • 嗨 timemirror 你有使用 SpeechStreamer 和 skype 的小示例代码吗?
    【解决方案3】:

    【讨论】:

      【解决方案4】:

      我最终缓冲了输入,然后将其以连续更大的块发送到语音识别引擎。例如,我可能先发送前 0.25 秒,然后发送前 0.5 秒,然后发送前 0.75 秒,以此类推,直到我得到结果。我不确定这是否是解决此问题的最有效方法,但它对我来说产生了令人满意的结果。

      祝你好运,肖恩

      【讨论】:

      • 我在使用 SAPI 和 MemoryStreams 时也遇到了问题。尽管从默认输入或文件中一切正常,但无法让它工作。当你说你使用缓冲区让它工作时,你的意思是你使用 Serguei 建议的 BufferStream 方法,还是你只是在 MemoryStream 更大之前阻止识别?我都试过了,都没有成功。您是否经常使用 SpeechHypothesized、SpeechRecognized 事件或强制 RecognitionResult rr = Recognizer.Recognize()?您可以发布更多代码来提供帮助吗?将不胜感激。
      【解决方案5】:

      您是否尝试过将网络流封装在 System.IO.BufferedStream 中?

      NetworkStream netStream = new NetworkStream(socket,true);
      BufferedStream buffStream = new BufferedStream(netStream, 8000*16*1); // buffers 1 second worth of data
      appRecognizer.SetInputToAudioStream(buffStream, formatInfo);
      

      【讨论】:

      • 您是否确认缓冲流支持查找?即,在上面的代码中,buffStream.CanSeek() 是否返回 true?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-21
      • 2018-12-31
      • 2019-07-25
      相关资源
      最近更新 更多