Reconocimiento de voz con el servicio de voz de Azure
Azure Speech Service es una API basada en la nube que ofrece la siguiente funcionalidad:
- Conversión de voz a texto transcribe archivos de audio o secuencias a texto.
- Texto a voz convierte el texto de entrada en voz sintetizada similar a la humana.
- Traducción de voz permite la traducción en tiempo real y en varios idiomas tanto para la conversión de voz a texto como para la conversión de voz a voz.
- Asistentes de voz pueden crear interfaces de conversación similares a las humanas para aplicaciones.
En este artículo se explica cómo se implementa la conversión de voz en texto en la aplicación de ejemplo Xamarin.Forms mediante Azure Speech Service. En las capturas de pantalla siguientes se muestra la aplicación de ejemplo en iOS y Android:
Creación de un recurso de Azure Speech Service
Azure Speech Service forma parte de Azure Cognitive Services, que proporciona API basadas en la nube para tareas como el reconocimiento de imágenes, el reconocimiento de voz y la traducción, y la búsqueda de Bing. Para más información, consulte ¿Qué es Azure Cognitive Services?.
El proyecto de ejemplo requiere que se cree un recurso de Azure Cognitive Services en Azure Portal. Se puede crear un recurso de Cognitive Services para un único servicio, como Speech Service o como un recurso de varios servicios. Los pasos para crear un recurso de Speech Service son los siguientes:
- Inicie sesión en Azure Portal.
- Cree un recurso de varios servicios o de un solo servicio.
- Obtenga la información de la clave de API y la región del recurso.
- Actualice el archivo Constants.cs de ejemplo.
Para obtener una guía paso a paso para crear un recurso, consulte Creación de un recurso de Cognitive Services.
Nota:
Si no tiene una suscripción a Azure, cree una cuenta gratuita antes de empezar. Una vez que tenga una cuenta, se puede crear un recurso de un solo servicio en el nivel gratuito para probar el servicio.
Configuración de la aplicación con Speech Service
Después de crear un recurso de Cognitive Services, el archivo Constants.cs se puede actualizar con la región y la clave de API del recurso de Azure:
public static class Constants
{
public static string CognitiveServicesApiKey = "YOUR_KEY_GOES_HERE";
public static string CognitiveServicesRegion = "westus";
}
Instalación del paquete Speech Service de NuGet
La aplicación de ejemplo usa el paquete NuGet de Microsoft.CognitiveServices.Speech para conectarse al Azure Speech Service. Instale este paquete NuGet en el proyecto compartido y en cada proyecto de plataforma.
Crear una interfaz IMicrophoneService
Cada plataforma requiere permiso para acceder al micrófono. El proyecto de ejemplo proporciona una interfaz IMicrophoneService
en el proyecto compartido y usa el Xamarin.FormsDependencyService
para obtener implementaciones de plataforma de la interfaz.
public interface IMicrophoneService
{
Task<bool> GetPermissionAsync();
void OnRequestPermissionResult(bool isGranted);
}
Crear el diseño de página
El proyecto de ejemplo define un diseño de página básico en el archivo MainPage.xaml. Los elementos de diseño clave son un Button
que inicia el proceso de transcripción, un Label
que contiene el texto transcrito y un ActivityIndicator
que se mostrará cuando la transcripción está en curso:
<ContentPage ...>
<StackLayout>
<Frame ...>
<ScrollView x:Name="scroll"
...>
<Label x:Name="transcribedText"
... />
</ScrollView>
</Frame>
<ActivityIndicator x:Name="transcribingIndicator"
IsRunning="False" />
<Button x:Name="transcribeButton"
...
Clicked="TranscribeClicked"/>
</StackLayout>
</ContentPage>
Implementación del servicio voz
El archivo de código subyacente de MainPage.xaml.cs contiene toda la lógica para enviar audio y recibir texto transcrito del Azure Speech Service.
El constructor MainPage
obtiene una instancia de la interfaz IMicrophoneService
de la DependencyService
:
public partial class MainPage : ContentPage
{
SpeechRecognizer recognizer;
IMicrophoneService micService;
bool isTranscribing = false;
public MainPage()
{
InitializeComponent();
micService = DependencyService.Resolve<IMicrophoneService>();
}
// ...
}
Se llama al método TranscribeClicked
cuando se pulsa la instancia transcribeButton
:
async void TranscribeClicked(object sender, EventArgs e)
{
bool isMicEnabled = await micService.GetPermissionAsync();
// EARLY OUT: make sure mic is accessible
if (!isMicEnabled)
{
UpdateTranscription("Please grant access to the microphone!");
return;
}
// initialize speech recognizer
if (recognizer == null)
{
var config = SpeechConfig.FromSubscription(Constants.CognitiveServicesApiKey, Constants.CognitiveServicesRegion);
recognizer = new SpeechRecognizer(config);
recognizer.Recognized += (obj, args) =>
{
UpdateTranscription(args.Result.Text);
};
}
// if already transcribing, stop speech recognizer
if (isTranscribing)
{
try
{
await recognizer.StopContinuousRecognitionAsync();
}
catch(Exception ex)
{
UpdateTranscription(ex.Message);
}
isTranscribing = false;
}
// if not transcribing, start speech recognizer
else
{
Device.BeginInvokeOnMainThread(() =>
{
InsertDateTimeRecord();
});
try
{
await recognizer.StartContinuousRecognitionAsync();
}
catch(Exception ex)
{
UpdateTranscription(ex.Message);
}
isTranscribing = true;
}
UpdateDisplayState();
}
El método TranscribeClicked
hace lo siguiente:
- Comprueba si la aplicación tiene acceso al micrófono y sale temprano si no lo hace.
- Crea una instancia de clase
SpeechRecognizer
si aún no existe. - Detiene la transcripción continua si está en curso.
- Inserta una marca de tiempo e inicia la transcripción continua si no está en curso.
- Notifica a la aplicación que actualice su apariencia en función del nuevo estado de la aplicación.
El resto de los métodos MainPage
de clase son asistentes para mostrar el estado de la aplicación:
void UpdateTranscription(string newText)
{
Device.BeginInvokeOnMainThread(() =>
{
if (!string.IsNullOrWhiteSpace(newText))
{
transcribedText.Text += $"{newText}\n";
}
});
}
void InsertDateTimeRecord()
{
var msg = $"=================\n{DateTime.Now.ToString()}\n=================";
UpdateTranscription(msg);
}
void UpdateDisplayState()
{
Device.BeginInvokeOnMainThread(() =>
{
if (isTranscribing)
{
transcribeButton.Text = "Stop";
transcribeButton.BackgroundColor = Color.Red;
transcribingIndicator.IsRunning = true;
}
else
{
transcribeButton.Text = "Transcribe";
transcribeButton.BackgroundColor = Color.Green;
transcribingIndicator.IsRunning = false;
}
});
}
El UpdateTranscription
método escribe el proporcionado newText
string
en el Label
elemento denominado transcribedText
. Obliga a que esta actualización se produzca en el subproceso de la interfaz de usuario para que se pueda llamar desde cualquier contexto sin causar excepciones. InsertDateTimeRecord
escribe la fecha y hora actuales en la instancia transcribedText
para marcar el inicio de una nueva transcripción. Por último, el método UpdateDisplayState
actualiza los elementos Button
y ActivityIndicator
para reflejar si la transcripción está en curso o no.
Creación de servicios de micrófono de plataforma
La aplicación debe tener acceso de micrófono para recopilar datos de voz. La interfaz IMicrophoneService
debe implementarse y registrarse con el DependencyService
en cada plataforma para que funcione la aplicación.
Android
El proyecto de ejemplo define una implementación IMicrophoneService
para Android denominada AndroidMicrophoneService
:
[assembly: Dependency(typeof(AndroidMicrophoneService))]
namespace CognitiveSpeechService.Droid.Services
{
public class AndroidMicrophoneService : IMicrophoneService
{
public const int RecordAudioPermissionCode = 1;
private TaskCompletionSource<bool> tcsPermissions;
string[] permissions = new string[] { Manifest.Permission.RecordAudio };
public Task<bool> GetPermissionAsync()
{
tcsPermissions = new TaskCompletionSource<bool>();
if ((int)Build.VERSION.SdkInt < 23)
{
tcsPermissions.TrySetResult(true);
}
else
{
var currentActivity = MainActivity.Instance;
if (ActivityCompat.CheckSelfPermission(currentActivity, Manifest.Permission.RecordAudio) != (int)Permission.Granted)
{
RequestMicPermissions();
}
else
{
tcsPermissions.TrySetResult(true);
}
}
return tcsPermissions.Task;
}
public void OnRequestPermissionResult(bool isGranted)
{
tcsPermissions.TrySetResult(isGranted);
}
void RequestMicPermissions()
{
if (ActivityCompat.ShouldShowRequestPermissionRationale(MainActivity.Instance, Manifest.Permission.RecordAudio))
{
Snackbar.Make(MainActivity.Instance.FindViewById(Android.Resource.Id.Content),
"Microphone permissions are required for speech transcription!",
Snackbar.LengthIndefinite)
.SetAction("Ok", v =>
{
((Activity)MainActivity.Instance).RequestPermissions(permissions, RecordAudioPermissionCode);
})
.Show();
}
else
{
ActivityCompat.RequestPermissions((Activity)MainActivity.Instance, permissions, RecordAudioPermissionCode);
}
}
}
}
AndroidMicrophoneService
tiene las siguientes características:
- El atributo
Dependency
registra la clase conDependencyService
. - El método
GetPermissionAsync
comprueba si se requieren permisos en función de la versión de Android SDK y llama aRequestMicPermissions
si aún no se ha concedido el permiso. - El método
RequestMicPermissions
usa la claseSnackbar
para solicitar permisos del usuario si se requiere una justificación; de lo contrario, solicita directamente permisos de grabación de audio. - Se llama al método
OnRequestPermissionResult
con un resultado debool
una vez que el usuario ha respondido a la solicitud de permisos.
La clase MainActivity
se personaliza para actualizar la instancia AndroidMicrophoneService
cuando se completan las solicitudes de permisos:
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
IMicrophoneService micService;
internal static MainActivity Instance { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
Instance = this;
// ...
micService = DependencyService.Resolve<IMicrophoneService>();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
// ...
switch(requestCode)
{
case AndroidMicrophoneService.RecordAudioPermissionCode:
if (grantResults[0] == Permission.Granted)
{
micService.OnRequestPermissionResult(true);
}
else
{
micService.OnRequestPermissionResult(false);
}
break;
}
}
}
La clase MainActivity
define una referencia estática denominada Instance
, que requiere el objeto AndroidMicrophoneService
al solicitar permisos. Reemplaza el método OnRequestPermissionsResult
para actualizar el objeto AndroidMicrophoneService
cuando el usuario aprueba o deniega la solicitud de permisos.
Por último, la aplicación Android debe incluir el permiso para grabar audio en el archivo AndroidManifest.xml:
<manifest ...>
...
<uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest>
iOS
El proyecto de ejemplo define una implementación IMicrophoneService
para iOS denominada iOSMicrophoneService
:
[assembly: Dependency(typeof(iOSMicrophoneService))]
namespace CognitiveSpeechService.iOS.Services
{
public class iOSMicrophoneService : IMicrophoneService
{
TaskCompletionSource<bool> tcsPermissions;
public Task<bool> GetPermissionAsync()
{
tcsPermissions = new TaskCompletionSource<bool>();
RequestMicPermission();
return tcsPermissions.Task;
}
public void OnRequestPermissionResult(bool isGranted)
{
tcsPermissions.TrySetResult(isGranted);
}
void RequestMicPermission()
{
var session = AVAudioSession.SharedInstance();
session.RequestRecordPermission((granted) =>
{
tcsPermissions.TrySetResult(granted);
});
}
}
}
iOSMicrophoneService
tiene las siguientes características:
- El atributo
Dependency
registra la clase conDependencyService
. - El método
GetPermissionAsync
llama aRequestMicPermissions
para solicitar permisos del usuario del dispositivo. - El método
RequestMicPermissions
usa la instanciaAVAudioSession
compartida para solicitar permisos de grabación. - El método
OnRequestPermissionResult
actualiza la instanciaTaskCompletionSource
con el valorbool
proporcionado.
Por último, la aplicación de iOS Info.plist debe incluir un mensaje que indique al usuario por qué la aplicación solicita acceso al micrófono. Edite el archivo Info.plist para incluir las siguientes etiquetas dentro del elemento <dict>
:
<plist>
<dict>
...
<key>NSMicrophoneUsageDescription</key>
<string>Voice transcription requires microphone access</string>
</dict>
</plist>
UWP
El proyecto de ejemplo define una implementación IMicrophoneService
para UWP denominada UWPMicrophoneService
:
[assembly: Dependency(typeof(UWPMicrophoneService))]
namespace CognitiveSpeechService.UWP.Services
{
public class UWPMicrophoneService : IMicrophoneService
{
public async Task<bool> GetPermissionAsync()
{
bool isMicAvailable = true;
try
{
var mediaCapture = new MediaCapture();
var settings = new MediaCaptureInitializationSettings();
settings.StreamingCaptureMode = StreamingCaptureMode.Audio;
await mediaCapture.InitializeAsync(settings);
}
catch(Exception ex)
{
isMicAvailable = false;
}
if(!isMicAvailable)
{
await Windows.System.Launcher.LaunchUriAsync(new Uri("ms-settings:privacy-microphone"));
}
return isMicAvailable;
}
public void OnRequestPermissionResult(bool isGranted)
{
// intentionally does nothing
}
}
}
UWPMicrophoneService
tiene las siguientes características:
- El atributo
Dependency
registra la clase conDependencyService
. - El método
GetPermissionAsync
intenta inicializar una instanciaMediaCapture
. Si se produce un error, inicia una solicitud de usuario para habilitar el micrófono. - El método
OnRequestPermissionResult
existe para satisfacer la interfaz, pero no es necesario para la implementación de UWP.
Por último, el Package.appxmanifest de UWP debe especificar que la aplicación use el micrófono. Haga doble clic en el archivo Package.appxmanifest y seleccione la opción Micrófono en la pestaña Funcionalidades de Visual Studio 2019:
Prueba de la aplicación
Ejecute la aplicación y haga clic en el botón Transcribir. La aplicación debe solicitar acceso al micrófono y comenzar el proceso de transcripción. El ActivityIndicator
se animará, mostrando que la transcripción está activa. Mientras habla, la aplicación transmitirá datos de audio al recurso de Azure Speech Services, que responderá con texto transcrito. El texto transcrito aparecerá en el elemento Label
a medida que se recibe.
Nota:
Los emuladores de Android no se cargan e inicializan las bibliotecas del servicio de voz. Se recomienda realizar pruebas en un dispositivo físico para la plataforma Android.