많은 작업을 마침으로써 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;
}
}
인터넷을 통해 오디오를 보낼 필요가 없습니다. 오프라인 음성 인식 엔진 [CMUSphinx] (http://cmusphinx.sourceforge.net)을 사용하여 즉각적인 응답을 보관할 수 있습니다. CMUSphinx 엔진 정확도는 Microsoft 엔진과 크게 다르지 않으며 Raspberry Pi 자체에서 완벽하게 작동합니다. –
Windows에서만 실행되는 고품질 음성 합성 엔진을 사용하고 있으므로 불행히도 Pi에 모든 것을 포함시킬 수는 없습니다. AI는 또한 Pi가 처리 할 수있는 것보다 더 많은 계산 집약적 인 작업을 수행 할 것입니다. – dgreenheck