c# - 如何通过 websocket 将音频发送到 Nexmo Voice

标签 c# websocket speech-recognition text-to-speech vonage

我正在尝试在 .Net Core 2 web api 中使用 websockets 实现 Nexmo 的语音 api。

这个 api 需要:
  • 通过Nexmo 接收来自电话的音频
  • 使用 Microsoft Cognitive Speech to text接口(interface)
  • 将文本发送给机器人
  • 使用 Microsoft Cognitive text to speech根据机器人的回复
  • 通过他们的语音 api websocket 将语音发送回 nexmo

目前,我正在绕过机器人步骤,因为我首先尝试连接到 websocket。 当尝试回声方法(将收到的音频发送回 websocket)时,它可以正常工作。 但是,当我尝试将语音从 Microsoft 文本发送到语音时,通话结束了。

我没有找到任何实现不同于回声的文档。

TextToSpeech 和 SpeechToText 方法在 websocket 外部使用时按预期工作。

这是带有语音到文本的 websocket:

public static async Task Echo(HttpContext context, WebSocket webSocket)
    {
        var buffer = new byte[1024 * 4];
        WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
        while (!result.CloseStatus.HasValue)
        {
            while(!result.EndOfMessage)
            {
                result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
            }
            var text = SpeechToText.RecognizeSpeechFromBytesAsync(buffer).Result;
            Console.WriteLine(text);
        }
        await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
    }

这是带有文字转语音功能的 websocket:

public static async Task Echo(HttpContext context, WebSocket webSocket)
    {
        var buffer = new byte[1024 * 4];
        WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
        while (!result.CloseStatus.HasValue)
        {
            var ttsAudio = await TextToSpeech.TransformTextToSpeechAsync("Hello, this is a test", "en-US");
            await webSocket.SendAsync(new ArraySegment<byte>(ttsAudio, 0, ttsAudio.Length), WebSocketMessageType.Binary, true, CancellationToken.None);

            result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
        }
        await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
    }

2019 年 3 月 1 日更新

回复 Sam Machin 的评论 我尝试将数组拆分为每个 640 字节的 block (我使用 16000khz 采样率),但 nexmo 仍然挂断电话,我仍然听不到任何声音。

public static async Task NexmoTextToSpeech(HttpContext context, WebSocket webSocket)
    {
        var ttsAudio = await TextToSpeech.TransformTextToSpeechAsync("This is a test", "en-US");
        var buffer = new byte[1024 * 4];
        WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

        while (!result.CloseStatus.HasValue)
        {
            await SendSpeech(context, webSocket, ttsAudio);
            result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
        }
        await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing Socket", CancellationToken.None);
    }

    private static async Task SendSpeech(HttpContext context, WebSocket webSocket, byte[] ttsAudio)
    {
        const int chunkSize = 640;
        var chunkCount = 1;
        var offset = 0;
        
        var lastFullChunck = ttsAudio.Length < (offset + chunkSize);
        try
        {
            while(!lastFullChunck)
            {
                await webSocket.SendAsync(new ArraySegment<byte>(ttsAudio, offset, chunkSize), WebSocketMessageType.Binary, false, CancellationToken.None);
                offset = chunkSize * chunkCount;
                lastFullChunck = ttsAudio.Length < (offset + chunkSize);
                chunkCount++;
            }

            var lastMessageSize = ttsAudio.Length - offset;
            await webSocket.SendAsync(new ArraySegment<byte>(ttsAudio, offset, lastMessageSize), WebSocketMessageType.Binary, true, CancellationToken.None);
        }
        catch (Exception ex)
        {
        }
    }

这是有时会出现在日志中的异常:

System.Net.WebSockets.WebSocketException (0x80004005): The remote party closed the WebSocket connection without completing the close handshake.

最佳答案

看起来你正在将整个音频剪辑写入 websocket,Nexmo 接口(interface)要求音频在 20ms 帧中每条消息一个,这意味着你需要将你的剪辑分成 320 或 640 字节(取决于on if you're using 8Khz or 16Khz) block 并将每个 block 写入套接字。如果您尝试将太大的文件写入套接字,它将如您所见关闭。

参见 https://developer.nexmo.com/voice/voice-api/guides/websockets#writing-audio-to-the-websocket了解详情。

关于c# - 如何通过 websocket 将音频发送到 Nexmo Voice,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54913102/

相关文章:

c# - 使用 LINQ 的列表集合的笛卡尔积的两种方法

node.js - Websocket 后端客户端连接在一段时间后停止接收流更新

java - 如何使用sphinx字典作为语法文件

java - Android语音识别服务空指针异常

java - IBM Watson 基于 session 的语音识别失败并出现 "Session does not exist"错误

c# - 如果处理失败,Rabbitmq 将消息移动到不同的队列

c# - 如何在没有 C# 引用的情况下将类公开给外部项目

Golang Gorilla Websocket 在 120 秒时停止接收信息

node.js - websockets - 拒绝套接字连接

c# - Span<T> 是否可以在没有固定表达式的情况下指向固定大小的缓冲区?