Потоковый ввод в System.Speech.Recognition.SpeechRecognitionEngine

Я пытаюсь выполнить «потоковое» распознавание речи на С# из сокета TCP. Проблема, с которой я сталкиваюсь, заключается в том, что SpeechRecognitionEngine.SetInputToAudioStream(), похоже, требует Stream определенной длины, который может искать. Прямо сейчас единственный способ, которым я могу думать, чтобы заставить эту работу работать, - это многократно запускать распознаватель в 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().

Спасибо, Шон


person spurserh    schedule 05.11.2009    source источник
comment
Возможно, SetInputToDefaultAudioDevice() — это черная магия Microsoft (распространенная), или она выполняет какую-то пакетную обработку, как вы предложили.   -  person R. Martinho Fernandes    schedule 05.11.2009


Ответы (5)


Я получил распознавание живой речи, переопределив класс потока:

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. Как только поток возвращает длину или возвращенное количество меньше запрошенного, механизм распознавания считает, что ввод завершен. Это устанавливает циклический буфер, который никогда не заканчивается.

person Sean    schedule 05.08.2012
comment
Привет, Шон, я пытался заставить ваше решение работать, но пока мне это не удалось. Как и в случае с другими выше, все отлично работает с файлом на диске, но просто не работает с MemoryStream. Вы иногда отправляете запрос на распознавание или можете использовать события SpeechHypothesized, SpeechRecognized? Не могли бы вы опубликовать еще код, чтобы помочь? Спасибо! - person timemirror; 23.08.2012
comment
Извините, пропустил ваш вопрос, вот и все. Благодаря этому я могу выполнять распознавание речи в режиме реального времени, а также транслировать аудиопоток по сети (часть моего проекта с открытым исходным кодом ispy — ispyconnect.com) - person Sean; 23.10.2012
comment
Спасибо, Шон... отличный проект. - person timemirror; 24.10.2012
comment
Ты гений Шон! Последняя версия вашего кода работает отлично! Захват выходных данных из Skype и работа с SAPI для распознавания речи. Большое спасибо за вашу помощь.... - person timemirror; 25.10.2012
comment
Привет, timemirror, у тебя есть небольшой пример кода, использующий SpeechStreamer со скайпом? - person Jean-Philippe Encausse; 20.06.2013
comment
@ Шон, эй, не могли бы вы подсказать мне, как использовать этот класс? Это кажется действительно многообещающим, и я хотел бы использовать его. Но как настроить его на использование существующего потока? - person Monacraft; 20.02.2018
comment
Если хотите, я могу задать этот вопрос еще раз (и таким образом отметить его как правильный). Извините, что выкопал это из прошлого ахахаха - person Monacraft; 20.02.2018
comment
@Monacraft только что прочитал из вашего существующего потока и записал в этот - person Sean; 20.02.2018
comment
Я научился распознавать устный текст на компьютере, используя это, но, к сожалению, для меня это совершенно фальшиво, пытаясь использовать методы NAudio... См. stackoverflow.com/questions /58678228 - person NoBugs; 03.11.2019

Вы пытались обернуть сетевой поток в 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);
person Serguei    schedule 05.11.2009
comment
Вы убедились, что буферизованный поток поддерживает поиск? То есть, в приведенном выше коде buffStream.CanSeek() возвращает true? - person Eric Brown; 16.11.2009

По-видимому, это невозможно («По замыслу»!). См. http://social.msdn.microsoft.com/Forums/en/netfxbcl/thread/fcf62d6d-19df-4ca9-9f1f-17724441f84e

person John Rusk - MSFT    schedule 20.11.2010

Это мое решение.

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;
person Hassen    schedule 09.06.2017

В итоге я буферизовал ввод, а затем отправлял его в механизм распознавания речи последовательно большими порциями. Например, я могу отправить сначала первые 0,25 секунды, затем первые 0,5 секунды, затем первые 0,75 секунды и так далее, пока не получу результат. Я не уверен, что это самый эффективный способ сделать это, но он дает удовлетворительные результаты для меня.

Удачи, Шон

person spurserh    schedule 09.01.2010
comment
У меня тоже проблемы с SAPI и MemoryStreams ... просто не могу заставить его работать, хотя все работает нормально из ввода по умолчанию или из файла. Когда вы сказали, что работаете с использованием буфера, вы имеете в виду, что используете подход BufferStream, предложенный Сергеем, или вы просто сдерживаете распознавание до тех пор, пока MemoryStream не станет больше? Я пробовал оба без успеха. Используете ли вы события SpeechHypothesized, SpeechRecognized или время от времени форсируете RecognitionResult rr = распознаватель.Recognize()? Можете ли вы опубликовать еще код, чтобы помочь? Был бы очень признателен. - person timemirror; 23.08.2012