https://d226lax1qjow5r.cloudfront.net/blog/blogposts/translate-a-phone-call-with-blazor-signalr-and-azure-dr/Blog_Translate_Phone-Call_1200x600.png

Traducir una llamada telefónica con Blazor, SignalR y Azure

Publicado el November 9, 2020

Tiempo de lectura: 17 minutos

Los modelos de aprendizaje automático nos permiten hacer todo tipo de cosas interesantes. Por ejemplo, traducción de voz en tiempo real. En este tutorial, aprenderemos a traducir una llamada recibida en un Numbers de Vonage. Traduciremos el discurso de la persona que llama y enviaremos todo el texto traducido a nuestro frontend. El uso de una aplicación WebAssembly Blazor alojada en .NET Core y SignalR hará que este proceso sea increíblemente fluido.

Requisitos previos

  • Necesitarás un Azure Speech Resource - puedes crear uno siguiendo los pasos aquí. Extraiga la región y el valor clave de la pestaña Keys and Endpoint de su recurso.

  • El último .NET Core SDK instalado

  • Visual Studio o Visual Studio Code. Voy a utilizar Visual Studio 2019 para esta demostración

  • Nuestra CLI. Si no la tiene, puede instalarla con npm install @vonage/cli -g

  • ngrok para hacer pruebas. Sólo necesitas la versión gratuita.

Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

Ir directamente al código

Si usted tiene todos los prereqs configuración, ya se puede saltar este tutorial y saltar directamente al código. La aplicación está en GitHub

Spin Up Ngrok

Vamos a utilizar ngrok para exponer a Internet nuestra aplicación ASP.NET Core que se ejecuta localmente. Después de haber instalado ngrok, hacerlo será tan fácil como ejecutar el comando ngrok http 5000 en tu consola. Eso producirá algo que se parece a esto:

Example of Ngrok running in the terminalExample of Ngrok running in the terminal

NOTA - este tutorial utiliza Kestrel para la depuración local. Si desea utilizar IIS Express en su lugar, consulte nuestro explicador - sobre el uso de ngrok con IIS Express.

Lo más importante a tener en cuenta aquí es la URL de reenvío - en mi caso, esa URL es http://1976e6d913a7.ngrok.io. Cuando recibes una llamada en tu número de Vonage, Vonage enviará a tu aplicación lo que se denomina un WebHook, que no es más que una solicitud HTTP GET, solicitando algo llamado objeto de control de llamadas Nexmo (NCCO). Nuestra aplicación estará escuchando en /webhooks/answer por lo que la URL completa que necesitaré será http://1976e6d913a7.ngrok.io/webhooks/answer.

Configurar CLI

Si aún no has configurado nuestra CLI, hazlo ejecutando el comando vonage config:set --apiKey=API_KEY --apiSecret=API_SECRET donde API Key y Secret son la clave y el secreto de la API que se encuentran en la página de configuración de su cuenta. página de configuración de su Account

Comprar un Numbers y crear una aplicación

Ahora que tu CLI está configurada, compraremos un número, crearemos una aplicación de Vonage y vincularemos el número a esa aplicación, lo que le indicará a Vonage que lo reenvíe a tu aplicación.

Comprar un Numbers

Para comprar un número, utilice el siguiente comando (sustituyendo el ID de su país por US)

vonage numbers:search US vonage numbers:buy [NUMBER] [COUNTRYCODE]

Escriba confirm para completar la operación; saldrá el número que haya comprado.

Crear una aplicación

A continuación, crearemos una aplicación. El comando crear aplicación tomará dos URL, la URL de respuesta, que será el número al que Vonage enviará las llamadas entrantes, y la URL de eventos, que será la URL a la que Vonage enviará los eventos que surjan de uno de tus números. Recuerda sustituir 1976e6d913a7 por cualquiera que sea el hash aleatorio para tu URL ngrok:

vonage apps:create ✔ Application Name … "DTMFInput" ✔ Select App Capabilities › Messages ✔ Create messages webhooks? … yes √ Answer Webhook - URL ... http://1976e6d913a7.ngrok.io/webhooks/answer √ Answer Webhook - Method » GET ✔ Status Webhook - URL … http://1976e6d913a7.ngrok.io/webhooks/events ✔ Status Webhook - Method › POST ✔ Allow use of data for AI training? Read data collection disclosure - https://help.nexmo.com/hc/en-us/articles/4401914566036 … yes

Esta operación responderá con un ID de aplicación y una clave privada. Guarde ambos valores. En este tutorial sólo utilizaremos el ID de la aplicación, pero la clave privada se utiliza para autorizar las solicitudes de la aplicación.

Vincular la solicitud

Luego, debemos vincular nuestro número recién comprado a nuestra aplicación. Vincular nuestro número le indicará a Vonage que envíe cualquier llamada recibida en ese número a la URL de webhook de nuestra aplicación. Para ello, necesitaremos el ID de la aplicación que acabamos de recibir de la solicitud de creación de aplicación, que será similar a e7a25242-77a1-42cd-a32e-09febcb375f4y el número de teléfono que acabamos de comprar, y ejecutaremos un comando similar a este:

vonage apps:link --number=VONAGE_NUMBER APP_ID

Cree nuestra aplicación

Ahora que nos hemos establecido y configurado, ahora tenemos que construir nuestra aplicación. Ve a tu directorio fuente típico en el terminal y ejecuta el siguiente comando:

dotnet new blazorwasm -ho --no-https -n VonageDotnetTranslator

Este comando creará una aplicación Blazor WebAssembly por ti. No vamos a configurar SSL, lo que facilitará el trabajo con ngrok.

Va a crear tres archivos csproj,

  1. VonageDotnetTranslator.Client - aquí es donde se define el WebAssembly - va a ser el frontend de nuestra aplicación.

  2. VonageDotnetTranslator.Server - Este será el servidor alojado en .NET core para nuestra aplicación. Este proyecto es donde ocurrirá la mayor parte de lo que necesitamos hacer.

  3. VonageDotnetTranslator.Shared - Estos son los datos compartidos entre el cliente y el servidor.

Añadir paquetes NuGet

Vamos a utilizar los siguientes paquetes NuGet para este ejemplo:

  1. Vonage

  2. Microsoft.aspnetcore.signalr.core

  3. Microsoft.CognitiveServices.Speech

  4. Microsoft.aspnetcore.signalr.client

Para instalarlos, vaya primero a VonageDotnetTranslator\Server en tu terminal y ejecuta los siguientes comandos:

dotnet add package Vonage dotnet add package Microsoft.aspnetcore.signalr.core dotnet add package Microsoft.CognitiveServices.Speech

A continuación, vaya a VonageDotnetTranslator\Client en tu terminal y ejecútalo:

dotnet add package Microsoft.AspNetcore.SignalR.client

Este comando instalará todos los paquetes que vas a necesitar. Ahora abre el archivo VonageDotnetTranslator.sln en Visual Studio.

Añadir un modelo

Vamos a crear un modelo de datos compartidos entre nuestro Cliente y Servidor. Para ello, vamos a utilizar una única clase, que llamaremos Translation. Esta clase contendrá un identificador único para la llamada que estamos traduciendo, el texto de un evento de traducción dado, el idioma hablado y el idioma traducido. Cree un archivo Translation.cs en el proyecto compartido y añádele lo siguiente:

public class Translation
 {
     public string UUID { get; set; }
     public string Text { get; set; }
     public string LanguageSpoken { get; set; }
     public string LanguageTranslated { get; set; }
 }

Añadir un centro de traducción

Vamos a recibir eventos de traducción en nuestro frontend usando una conexión SignalR. Para comunicarnos a través de nuestro frontend desde nuestro backend, vamos a utilizar una conexión Hub. Para utilizar esto, tenemos que definir un Hub en nuestro servidor. Sólo tienes que añadir una carpeta Hubs a nuestro proyecto de servidor, a continuación, añadir una clase llamada TranslationHubque sea una clase pública y que herede de Microsoft.AspNetCore.SignalR.Hubs. No necesitamos ninguna otra lógica para esta clase.

Construya nuestro traductor

La clase más compleja de este proyecto va a ser nuestro traductor. Al que vamos a llamar TranslationEngine. Empecemos creando un archivo TranslationEngine.cs en nuestro VonageDotnetTranslator.Server proyecto. El TranslationEngine va a manejar algunos recursos subyacentes del sistema. En consecuencia, el TranslationEngine implementará la interfaz IDisposable para permitirnos limpiarlo después de que haya terminado fácilmente. La definición de la clase debería tener este aspecto:

public class TranslationEngine : IDisposable

Definir constantes

Vamos a añadir varias constantes a esta clase para empezar. Estas constantes van a ser varios metadatos sobre el flujo de audio que vamos a manejar. Vamos a añadir un SAMPLES_PER_SECOND a 16000, un BITS_PER_SAMPLE a 16, a NUMBER_OF_CHANNELS a 1, y a BUFFER_SIZE a 640 (o 320 * 2).

const int SAMPLES_PER_SECOND = 16000;
const int BITS_PER_SAMPLE = 16;
const int NUMBER_OF_CHANNELS = 1;
const int BUFFER_SIZE = 320 * 2;

Añadir campos privados

La clase TranslationEngine tiene un buen número de partes móviles. Tendremos que definir un montón de campos privados. La mayoría de estos campos manejan la configuración del traductor y del sintetizador de voz. Tenemos un par que manejarán los metadatos para el motor de traducción. También tendremos una cola concurrente donde pondremos en cola el audio para volver a escribirlo en nuestra llamada. Independientemente de los campos se verá así:

private ConcurrentQueue<byte[]> _audioToWrite = new ConcurrentQueue<byte[]>(); // queue to managed synthesized audio
private readonly IConfiguration _config; //Where Azure Subscription Keys will be stored
private readonly IHubContext<TranslationHub> _hub; // Hub connection we'll use to talk to frontend
private string _uuid; // Unique ID of the call being translated
private string _languageSpoken; // The language being spoken on the call
private string _languageTranslated; // The language being translated to

private SpeechTranslationConfig _translationConfig; // the configuration for the speech translator
private SpeechConfig _speechConfig; // configuration for the speech synthesizer
private PushAudioInputStream _inputStream = AudioInputStream.CreatePushStream(AudioStreamFormat.GetWaveFormatPCM(SAMPLES_PER_SECOND, BITS_PER_SAMPLE, NUMBER_OF_CHANNELS)); //Stream for handling audio input to the translator
private AudioConfig _audioInput; //configuration for the translation audio
private TranslationRecognizer _recognizer; // The translator
private SpeechSynthesizer _synthesizer; // The syntheziser, which will turn translated text into audio
private AudioOutputStream _audioOutputStream; // Output stream from the synthezier
private AudioConfig _outputConfig; // output configuration for the speech syntheizer

Requiere un montón de importaciones, aquí están las importaciones que estoy usando en esta clase:

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SignalR;
using Microsoft.CognitiveServices.Speech;
using Microsoft.CognitiveServices.Speech.Audio;
using Microsoft.CognitiveServices.Speech.Translation;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using VonageDotnetTranslator.Server.Hubs;
using VonageDotnetTranslator.Shared;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;

Añadir Constructor

Vamos a tener un único constructor para esta clase. Va a estar recibiendo dos elementos de tipo inyección de dependencia del middleware. Un objeto IConfiguration object, que es donde almacenaremos las Credenciales Azure de nuestro recurso Cognitive Services y un IHubContext object, que utilizaremos para comunicarnos con nuestro frontend. Asignaremos estos a los campos de clase apropiados, y luego también construiremos algunas configuraciones y flujos para nuestro audio.

public TranslationEngine(IConfiguration config, IHubContext<TranslationHub> hub)
{
   _hub = hub;
   _config = config;
   _translationConfig = SpeechTranslationConfig.FromSubscription(
       _config["SUBSCRIPTION_KEY"], _config["REGION"]);
   _speechConfig = SpeechTranslationConfig.FromSubscription(
       _config["SUBSCRIPTION_KEY"], _config["REGION"]);
   _audioInput = AudioConfig.FromStreamInput(_inputStream);
   _audioOutputStream = AudioOutputStream.CreatePullStream();
   _outputConfig = AudioConfig.FromStreamOutput(_audioOutputStream);
}

Reconocimiento de traducciones

Ahora necesitaremos añadir un evento para manejar los eventos de reconocimiento de traducción. Cada vez que nuestro traductor traduzca una parte del discurso, disparará este evento. Sacaremos la traducción del evento de reconocimiento. A continuación, se alimenta a través de nuestro SpeechSynthesizer para extraer el audio que se reproducirá a nuestro usuario a través de la llamada. A continuación, vamos a construir un Translation a partir del texto traducido y lo enviaremos a todos los clientes que estén escuchando el concentrador. Por último, pondremos en cola el audio sintetizado en la cola que creamos anteriormente.

private void RecognizerRecognized(object sender, TranslationRecognitionEventArgs e)
{
   var translationLanguage = _languageTranslated.Split("-")[0];
   var translation = e.Result.Translations[translationLanguage].ToString();
   Trace.WriteLine("Recognized: " + translation);
   var ttsAudio = _synthesizer.SpeakTextAsync(translation).Result.AudioData;
   var translationResult = new Translation
   {
       LanguageSpoken = _languageSpoken,
       LanguageTranslated = _languageTranslated,
       Text = translation,
       UUID = _uuid
   };
   _hub.Clients.All.SendAsync("receiveTranslation", translationResult);
   _audioToWrite.Enqueue(ttsAudio);
}

Iniciar el traductor y el sintetizador

Cuando recibamos una llamada, vamos a poner en marcha nuestro traductor y sintetizador. Vamos a registrar nuestro RecognizerRecognized con el traductor y dejaremos que todo se inicie continuamente. Dado que estamos utilizando un flujo de entrada para alimentar el audio en nuestro traductor, esto empujará continuamente eventos de traducción después de un tiempo determinado ha transcurrido, o el traductor detecta una interrupción en el habla.

private async Task StartSpeechTranslationEngine(string recognitionLanguage, string targetLanguage)
{
   _translationConfig.SpeechRecognitionLanguage = recognitionLanguage;
   _translationConfig.AddTargetLanguage(targetLanguage);
   _speechConfig.SpeechRecognitionLanguage = targetLanguage;
   _speechConfig.SpeechSynthesisLanguage = targetLanguage;
   _synthesizer = new SpeechSynthesizer(_speechConfig, _outputConfig);
   _recognizer = new TranslationRecognizer(_translationConfig, _audioInput);
   _recognizer.Recognized += RecognizerRecognized;
   await _recognizer.StartContinuousRecognitionAsync();
}

Detener la traducción

Necesitaremos un método para detener el motor de traducción. Este método dará de baja nuestro evento RecognizerRecognized del archivo _recognizer y llamará al método StopContinuousRecognitionAsync en el reconocedor para cerrarlo. Este método puede tardar varios segundos en cerrarse, por eso lo hacemos de forma asíncrona.

private async Task StopTranscriptionEngine()
{
   if (_recognizer != null)
   {
       _recognizer.Recognized -= RecognizerRecognized;
       await _recognizer.StopContinuousRecognitionAsync();
   }
}

Bucle de procesamiento principal

Estaremos recibiendo audio de nuestras llamadas en un WebSocket, lo que significa que estaremos continuamente leyendo audio del WebSocket. Entonces, cada vez que obtengamos traducciones, estaremos tomando el audio sintetizado de nuestro SpeechSynthesizer y lo escribiremos de vuelta a través del WebSocket para que la persona que llama pueda escuchar la traducción. Este proceso ocurrirá en un bucle principal que continuará leyendo mensajes del WebSocket hasta que veamos un estado de cierre.

En particular, el primer mensaje que recibamos del WebSocket será un JSON codificado en UTF-8 que corresponde al archivo Headers que luego pasaremos a la Voice API de Vonage cuando le pidamos que cree el WebSocket por nosotros. Ese Headers contendrá un objeto Translation que definimos en nuestro proyecto compartido, por lo que deserializaremos el JSON en un objeto Translation y usaremos los metadatos para activar nuestro TranslationEngine.

public async Task ReceiveAudioOnWebSocket(HttpContext context, WebSocket webSocket)
{
   var buffer = new byte[BUFFER_SIZE];

   try
   {
       WebSocketReceiveResult result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
       var config = JsonConvert.DeserializeObject<Translation>(System.Text.Encoding.Default.GetString(buffer));
       _uuid = config.UUID;
       await StartSpeechTranslationEngine(config.LanguageSpoken,
           config.LanguageTranslated);
       _languageSpoken = config.LanguageSpoken;
       _languageTranslated = config.LanguageTranslated;
       while (!result.CloseStatus.HasValue)
       {

           byte[] audio;
           while (_audioToWrite.TryDequeue(out audio))
           {
               const int bufferSize = 640;
               for (var i = 0; i + bufferSize < audio.Length; i += bufferSize)
               {
                   var audioToSend = audio[i..(i + bufferSize)];
                   var endOfMessage = audio.Length > (bufferSize + i);
                   await webSocket.SendAsync(new ArraySegment<byte>(audioToSend, 0, bufferSize), WebSocketMessageType.Binary, endOfMessage, CancellationToken.None);
               }
           }

           result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);

           _inputStream.Write(buffer);
       }
       await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);
   }
   catch (Exception e)
   {
       Trace.WriteLine(e.ToString());
   }
   finally
   {
       await StopTranscriptionEngine();
   }
}

Deshágase de todo

Por último, seremos buenos ciudadanos de la memoria y nos desharemos de todos los recursos no gestionados a los que hayamos accedido durante nuestra traducción cuando se destruya este objeto.

public void Dispose()
{
   _inputStream.Dispose();
   _audioInput.Dispose();
   _recognizer.Dispose();
   _synthesizer.Dispose();
   _audioOutputStream.Dispose();
}

Añadir un controlador de voz

A partir de aquí todo es cuesta abajo. Por ahora, vamos a añadir un controlador de API vacío llamado VoiceController a nuestra carpeta Controllers carpeta. Aquí, vamos a añadir una sola ruta llamada Answer. El endpoint será /webhooks/answer. Este método será una solicitud GET a la que se llamará cuando tu número de API de Vonage reciba una llamada. Creará un objeto de control de llamadas Nexmo (NCCO) que le indicará a Vonage que cree un WebSocket para nuestro servidor. Como señalamos anteriormente, pasaremos un Translation a través de las Cabeceras de este objeto, que nos dirá en el otro lado cómo manejar la traducción. Tomaremos este NCCO y le devolveremos su JSON a Vonage para indicarle cómo manejar la solicitud.

[Route("/webhooks/answer")]
[HttpGet]
public ActionResult Answer()
{
   var host = Request.Host.ToString();
   var webSocketAction = new ConnectAction()
   {
       Endpoint = new[]
       {
           new WebsocketEndpoint()
           {
               Uri = $"ws://{host}/ws",
               ContentType="audio/l16;rate=16000",
               Headers = new Translation
               {
                   UUID = Request.Query["uuid"].ToString(),
                   LanguageSpoken = "en-US",
                   LanguageTranslated = "es-MX"
               }
           }
       }
   };
   var ncco = new Ncco(webSocketAction);
   return Ok(ncco.ToString());
}

Configurar middleware

Ahora que tenemos un controlador, lo último que hay que hacer en la parte Servidor de nuestra aplicación es configurar el middleware de la aplicación. Abre Startup.cs.

ConfigureServices

Dentro del método ConfigureServices añada una llamada para añadir SignalR:

services.AddSignalR();

app.UseEndpoints

A continuación, tendremos que hacer algunas modificaciones significativas en la app.UseEndpoints en el método Configure método. En primer lugar, definiremos una ruta a nuestro hub en /TranslationHub que podrán utilizar nuestros clientes:

endpoints.MapHub<Hubs.TranslationHub>("/TranslationHub");

A continuación, configuraremos las opciones de WebSocket para manejar los mensajes de Vonage del tamaño adecuado según la codificación PCM lineal de 16khz que estamos utilizando:

var webSocketOptions = new WebSocketOptions()
{
  KeepAliveInterval = TimeSpan.FromSeconds(120),
  ReceiveBufferSize = 640
};

app.UseWebSockets(webSocketOptions);

Por último, definiremos una ruta directa a nuestros WebSockets en el archivo app.UseEndpoints delegado. Esta ruta extraerá el HubContext para nuestro TranslationHubactualizará el WebSocket, y arrancará nuestro motor de Traducción con el hub, la configuración, el WebSocket, y el httpContext que necesita para ponerse en marcha.

endpoints.Map("/ws", async (context) => {
if (context.WebSockets.IsWebSocketRequest)
{
   var hub = (IHubContext<TranslationHub>)app.ApplicationServices.GetService(typeof(IHubContext<TranslationHub>));
   WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();
   using (var engine = new TranslationEngine(Configuration, hub))
   {
       await engine.ReceiveAudioOnWebSocket(context, webSocket);
   }
}
else
{
   context.Response.StatusCode = 400;
}
});

Construir nuestro frontend

La última cosa que vamos a necesitar hacer es construir nuestro frontend. Irónicamente esta es la parte fácil de este ejercicio en el proyecto VonageDotnetTranslator.Client añade un nuevo componente Razor a la carpeta Pages llamado TranslationComponent.razor. Este archivo es donde vamos a definir nuestra lógica frontend.

Incorporación de dependencias

Vamos a extraer las dependencias que necesitamos para trabajar con este componente de traducción. Estas dependencias incluirán el cliente SignalR, el proyecto compartido que hemos estado utilizando, así como la inyección de un archivo NavigationManager (para ayudar con el enrutamiento), y finalmente la implementación de IDisposable:

@using Microsoft.AspNetCore.SignalR.Client
@using VonageDotnetTranslator.Shared
@inject NavigationManager NavigationManager
@implements IDisposable

Añadir código para actualizar las traducciones

A continuación, en el bloque @code definiremos un diccionario para guardar todas las traducciones que recibamos de nuestro servidor, y tendremos un bloque HubConnection. En OnInitializedAsyncconstruiremos nuestro HubConnectionapuntando a nuestra ruta /TranslationHub definida en el middleware. Entonces, cada vez que el evento receiveTranslation (este es el evento que enviamos cuando se produce una traducción), actualizaremos nuestro diccionario de traducción. Si la UUID de la llamada ya está en el diccionario, concatenaremos el texto de la traducción al texto de la traducción actual. Si no, añadiremos un nuevo objeto de traducción. A continuación, iniciaremos la Conexión Hub y añadiremos un par de métodos extra para gestionar la conexión y limpiarla cuando termine.

@code {
    private Dictionary<string, Translation> _translations = new Dictionary<string, Translation>();
    private HubConnection _hubConnection;
    protected override async Task OnInitializedAsync()
    {
        _hubConnection = new HubConnectionBuilder()
            .WithUrl(NavigationManager.ToAbsoluteUri("/TranslationHub"))
            .Build();
        _hubConnection.On<Translation>("receiveTranslation", (translation) =>
        {
            if (_translations.ContainsKey(translation.UUID))
            {
                _translations[translation.UUID].Text += translation.Text;
            }
            else
            {
                _translations.Add(translation.UUID, translation);
            }
            StateHasChanged();
        });
        await _hubConnection.StartAsync();
    }
    public bool IsConnected => _hubConnection.State == HubConnectionState.Connected;

    public void Dispose()
    {
        _ = _hubConnection.DisposeAsync();
    }
}

Añadir nuestro punto de vista

A continuación, añadiremos una Tabla que contendrá todas nuestras traducciones. Esa tabla tendrá encabezados correspondientes a las propiedades del objeto Translation y la rellenaremos directamente desde nuestra _translations colección.

<h3>Translation</h3>
<table class="table">
    <thead>
        <tr>
            <th>Uuid</th>
            <th>Language Spoken</th>
            <th>Language Translated To</th>
            <th>Text</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var translation in _translations.Values)
        {
            <tr>
                <td>@translation.UUID</td>
                <td>@translation.LanguageSpoken</td>
                <td>@translation.LanguageTranslated</td>
                <td>@translation.Text</td>
            </tr>
        }
    </tbody>
</table>

Añadir al índice

Lo último que hay que hacer es añadir el TranslationComponent al archivo index.razor archivo. Para ello, abra Index.razorelimine su contenido, excepto la directiva @page y añádalo:

Configurar la aplicación

Lo último que hay que hacer es añadir los dos elementos de configuración de Azure al archivo VonageDotnetTranslation.Server/appsettings.json archivo. Añade dos campos al objeto base SUBSCRIPTION_KEY y REGIONy ajústalos a tu clave de suscripción y región.

Prueba

¡Eso es todo lo que necesitas para construir tu traductor de voz! Ahora todo lo que queda por hacer es probarlo y ejecutar la aplicación desde el directorio VonageDotnetTranslation/Server con dotnet run o utiliza f5 o el botón de reproducción.

Nota - Si utiliza IIS Express, deberá consultar nuestra guía para utilizar ngrok con IIS Express

En nuestro VoiceController hemos puesto como idioma de traducción el español, y como idioma hablado el inglés de EE.UU. - puede cambiarlo fácilmente - vea la sección idiomas soportados para saber qué idiomas soporta Azure.

Llegar más lejos

Los WebSockets permiten todo tipo de potentes casos de uso cuando se combinan con Azure Cognitive Services. Además de traducciones, también puedes hacer transcripciones normales o incluso análisis de sentimiento. Las API de Vonage permiten todo tipo de integraciones muy flexibles y relativamente fáciles de crear con PSTN y VoIP.

Recursos

  • El código fuente de esta demo está en GitHub

Compartir:

https://a.storyblok.com/f/270183/384x384/73d57fd8eb/stevelorello.png
Steve LorelloAntiguos alumnos de Vonage

Antiguo desarrollador .NET Advocate @Vonage, ingeniero de software poliglota full-stack, AI/ML