2013-04-08 3 views
4

다음 System.Speech.SpeechRecognitionEngine 에 스트리밍 C#을 C 번호 - RTP 스트림을 캡처 및 음성 인식에 보내 내가 달성하기 위해 노력하고 무엇

  • 앞으로의

    • 캡처 RTP 스트림

      저는 마이크로폰 입력을 받아들이는 Linux 기반 로봇을 만들고, Microsoft 음성 인식을 사용하여 오디오를 처리하고 로봇에 응답을 되돌려주는 Windows 컴퓨터를 보냅니다. 로봇은 서버로부터 수 백 마일 떨어져있을 수도 있으므로 인터넷을 통해 로봇을 테스트하고 싶습니다.

      내가 지금까지했던 어떤 :

      • 는 로봇이는 FFmpeg을 사용하여 (다른 형식 가능) MP3 포맷으로 인코딩은 RTP 스트림을 생성 되세요 (로봇이 아치 리눅스를 실행하는 라즈베리 파이에서 실행) VLC ActiveX 컨트롤을 사용하여 클라이언트 컴퓨터에서
      • 캡처 스트림은
      • SpeechRecognitionEngine이 가능한 방법이 있는지 찾았
        1. recognizer.SetInputToWaveSt 연()
        2. recognizer.SetInputToAudioStream()
        3. recognizer.SetInputToDefaultAudioDevice()
        4. 라인 - 앱의 출력을 전송하는 단자를 사용 바라보며
      • 하지만 완전히 혼란 하였다. 나는 실제로 SpeechRecognitionEngine에 VLC에서 스트림을 전송하는 방법에 붙어

        :

      나는 도움이 필요 무엇. VLC는 스트림을 전혀 노출하지 않습니다. 스트림을 캡처하여 해당 스트림 개체를 SpeechRecognitionEngine에 전달할 수있는 방법이 있습니까? 아니면 RTP가 해결책이 아닌가?

      미리 도움을 주셔서 감사합니다.

  • +0

    인터넷을 통해 오디오를 보낼 필요가 없습니다. 오프라인 음성 인식 엔진 [CMUSphinx] (http://cmusphinx.sourceforge.net)을 사용하여 즉각적인 응답을 보관할 수 있습니다. CMUSphinx 엔진 정확도는 Microsoft 엔진과 크게 다르지 않으며 Raspberry Pi 자체에서 완벽하게 작동합니다. –

    +0

    Windows에서만 실행되는 고품질 음성 합성 엔진을 사용하고 있으므로 불행히도 Pi에 모든 것을 포함시킬 수는 없습니다. AI는 또한 Pi가 처리 할 수있는 것보다 더 많은 계산 집약적 인 작업을 수행 할 것입니다. – dgreenheck

    답변

    4

    많은 작업을 마침으로써 Microsoft.SpeechRecognitionEngine에 WAVE 오디오 스트림을 허용하게되었습니다. 과정은 다음과 같습니다.

    파이에서 나는 ffmpeg를 실행 중입니다. 나는 서버 측에서이 명령

    ffmpeg -ac 1 -f alsa -i hw:1,0 -ar 16000 -acodec pcm_s16le -f rtp rtp://XXX.XXX.XXX.XXX:1234 
    

    를 사용하여 오디오를 스트리밍, 나는 UDPClient를 작성하고 포트 1234에 나는 별도의 스레드에서 패킷을 수신 들어요. 먼저 RTP 헤더 (header format explained here)를 제거하고 페이로드를 특수 스트림에 씁니다. SpeechRecognitionEngine이 작동하려면 SpeechStreamer 클래스 described in Sean's response을 사용해야했습니다. 표준 Memory Stream으로 작동하지 않았습니다.

    음성 인식 측면에서해야 할 일은 기본 오디오 장치 대신 오디오 스트림에 입력을 설정하는 것입니다.

    recognizer.SetInputToAudioStream(rtpClient.AudioStream, 
        new SpeechAudioFormatInfo(WAVFile.SAMPLE_RATE, AudioBitsPerSample.Sixteen, AudioChannel.Mono)); 
    

    나는 (즉,이 일을 위해 스트리밍시키는 그것은 여전히 ​​작동하는지보고) 그것을 광범위한 테스트를 완료하지 않은,하지만 난 SpeechRecognized의 오디오 샘플을 저장할 수있어 그것은 큰 소리가 난다. 16KHz의 샘플 속도를 사용하고 있습니다. 데이터 전송량을 줄이기 위해 8KHz까지 떨어 뜨릴 수도 있지만 일단 문제가되면 걱정할 것입니다.

    응답도 매우 빠릅니다. 나는 전체 문장을 말할 수 있고 1 초 이내에 응답을 얻을 수 있습니다. RTP 연결은 프로세스에 약간의 오버 헤드를 추가하는 것으로 보입니다. 벤치 마크를 시도하고 MIC 입력을 사용하는 것과 비교해야 할 것입니다.

    편집 : 여기 내 RTPClient 클래스입니다.

    /// <summary> 
        /// Connects to an RTP stream and listens for data 
        /// </summary> 
        public class RTPClient 
        { 
         private const int AUDIO_BUFFER_SIZE = 65536; 
    
         private UdpClient client; 
         private IPEndPoint endPoint; 
         private SpeechStreamer audioStream; 
         private bool writeHeaderToConsole = false; 
         private bool listening = false; 
         private int port; 
         private Thread listenerThread; 
    
         /// <summary> 
         /// Returns a reference to the audio stream 
         /// </summary> 
         public SpeechStreamer AudioStream 
         { 
          get { return audioStream; } 
         } 
         /// <summary> 
         /// Gets whether the client is listening for packets 
         /// </summary> 
         public bool Listening 
         { 
          get { return listening; } 
         } 
         /// <summary> 
         /// Gets the port the RTP client is listening on 
         /// </summary> 
         public int Port 
         { 
          get { return port; } 
         } 
    
         /// <summary> 
         /// RTP Client for receiving an RTP stream containing a WAVE audio stream 
         /// </summary> 
         /// <param name="port">The port to listen on</param> 
         public RTPClient(int port) 
         { 
          Console.WriteLine(" [RTPClient] Loading..."); 
    
          this.port = port; 
    
          // Initialize the audio stream that will hold the data 
          audioStream = new SpeechStreamer(AUDIO_BUFFER_SIZE); 
    
          Console.WriteLine(" Done"); 
         } 
    
         /// <summary> 
         /// Creates a connection to the RTP stream 
         /// </summary> 
         public void StartClient() 
         { 
          // Create new UDP client. The IP end point tells us which IP is sending the data 
          client = new UdpClient(port); 
          endPoint = new IPEndPoint(IPAddress.Any, port); 
    
          listening = true; 
          listenerThread = new Thread(ReceiveCallback); 
          listenerThread.Start(); 
    
          Console.WriteLine(" [RTPClient] Listening for packets on port " + port + "..."); 
         } 
    
         /// <summary> 
         /// Tells the UDP client to stop listening for packets. 
         /// </summary> 
         public void StopClient() 
         { 
          // Set the boolean to false to stop the asynchronous packet receiving 
          listening = false; 
          Console.WriteLine(" [RTPClient] Stopped listening on port " + port); 
         } 
    
         /// <summary> 
         /// Handles the receiving of UDP packets from the RTP stream 
         /// </summary> 
         /// <param name="ar">Contains packet data</param> 
         private void ReceiveCallback() 
         { 
          // Begin looking for the next packet 
          while (listening) 
          { 
           // Receive packet 
           byte[] packet = client.Receive(ref endPoint); 
    
           // Decode the header of the packet 
           int version = GetRTPHeaderValue(packet, 0, 1); 
           int padding = GetRTPHeaderValue(packet, 2, 2); 
           int extension = GetRTPHeaderValue(packet, 3, 3); 
           int csrcCount = GetRTPHeaderValue(packet, 4, 7); 
           int marker = GetRTPHeaderValue(packet, 8, 8); 
           int payloadType = GetRTPHeaderValue(packet, 9, 15); 
           int sequenceNum = GetRTPHeaderValue(packet, 16, 31); 
           int timestamp = GetRTPHeaderValue(packet, 32, 63); 
           int ssrcId = GetRTPHeaderValue(packet, 64, 95); 
    
           if (writeHeaderToConsole) 
           { 
            Console.WriteLine("{0} {1} {2} {3} {4} {5} {6} {7} {8}", 
             version, 
             padding, 
             extension, 
             csrcCount, 
             marker, 
             payloadType, 
             sequenceNum, 
             timestamp, 
             ssrcId); 
           } 
    
           // Write the packet to the audio stream 
           audioStream.Write(packet, 12, packet.Length - 12); 
          } 
         } 
    
         /// <summary> 
         /// Grabs a value from the RTP header in Big-Endian format 
         /// </summary> 
         /// <param name="packet">The RTP packet</param> 
         /// <param name="startBit">Start bit of the data value</param> 
         /// <param name="endBit">End bit of the data value</param> 
         /// <returns>The value</returns> 
         private int GetRTPHeaderValue(byte[] packet, int startBit, int endBit) 
         { 
          int result = 0; 
    
          // Number of bits in value 
          int length = endBit - startBit + 1; 
    
          // Values in RTP header are big endian, so need to do these conversions 
          for (int i = startBit; i <= endBit; i++) 
          { 
           int byteIndex = i/8; 
           int bitShift = 7 - (i % 8); 
           result += ((packet[byteIndex] >> bitShift) & 1) * (int)Math.Pow(2, length - i + startBit - 1); 
          } 
          return result; 
         } 
        } 
    
    +0

    Windows에서 VLC 또는 Windows에서 ffmpeg를 사용하여 RTPClient를 테스트하는 방법에 대한 아이디어가 있습니까? –

    1

    나는 더 단순하게 유지해야한다고 생각합니다. RTP와 특수 라이브러리를 사용하여 RTP를 캡처하는 이유는 무엇입니까? Rasperry Pi의 오디오 데이터를 가져 와서 Http Post를 사용하여 서버로 보내지 않는 이유는 무엇입니까?

    System.Speech는 MP3 형식을 지원하지 않습니다. 도움이 될 수 있습니다 - Help with SAPI v5.1 SpeechRecognitionEngine always gives same wrong result with C#. System.Speech 오디오는 PCM, ULaw 또는 ALaw 형식이어야합니다. 인식기에서 지원하는 형식을 확인하는 가장 확실한 방법은 RecognizerInfo.SupportedAudioFormats를 사용하여 해당 형식을 조사하는 것입니다.

    그런 다음 서버에 데이터를 게시 할 수 있습니다 (ContentType = "audio/x-wav"사용). 요청에 오디오 매개 변수를 포함 시키려면

    http://server/app/recognize/{sampleRate}/{bits}/{isStereo} 
    

    과 같은 URL 형식을 사용했습니다. 캡처 한 wav 파일을 POST 본문에 보냅니다.

    하나의 catch는 우리가 System.Speech로 보내기 전에 WAV 파일 헤더를 데이터에 추가해야한다는 것을 의미합니다. 우리의 데이터는 PCM 이었지만 WAV 형식은 아닙니다. 이 작업을 수행해야 할 경우에 대비하여 https://ccrma.stanford.edu/courses/422/projects/WaveFormat/을 참조하십시오.

    +0

    wav 파일을 보내는 것 외에는 즉각적인 응답을 보관할 수 없습니다. 전송 작업에 시간이 걸리고 응답에 시간이 걸립니다. 제대로 구현 된 RTP 기반 솔루션 또는 더 나은 경우 MRCP는 전체 파일을 전송하는 구현보다 10 배나 작은 응답 시간을 제공 할 수 있습니다. –

    +1

    사실이지만, 인터넷을 통해 음성 명령을 수신 할 수있는 로봇의 경우, 데스크톱 Windows 컴퓨터에서 처리되는 경우 (System.Speech가 사용 중이므로 가정), 추가 지연이 문제가되지 않을 것이라고 생각합니다. . –

    +0

    응답 해 주셔서 감사합니다. 실제로 RTP를 통해 작동하게되었습니다. 나는 다른 사람들이 이것을 어떻게하는지를보기 위해 내가했던 것을 게시 할 것이다. – dgreenheck

    0

    오래된 스레드이지만 작업하고있는 프로젝트에 유용했습니다. 그러나 Windows PC를 소스로 사용하여 dgreenheck의 코드를 사용하려는 다른 사람들과 동일한 문제가있었습니다.

    있어 FFMPEG 다음 매개 변수를 사용하여 코드에 그 0 변경 작업 : 내 경우

    ffmpeg -ac 1 -f dshow -i audio="{recording device}" -ar 16000 -acodec pcm_s16le -f rtp rtp://{hostname}:{port} 
    

    를, 기록 장치의 이름은 "마이크 (리얼텍 HD 오디오)"이었다,하지만 난 다음을 사용 녹음 장치 이름을 얻으려면 :

    ffmpeg -list_devices true -f dshow -i dummy 
    
    관련 문제