Reconhecimento de fala usando o Serviço de Fala do Azure

Baixar exemplo Baixar o exemplo

O Serviço de Fala do Azure é uma API baseada em nuvem que oferece a seguinte funcionalidade:

  • A conversão de fala em texto transcreve arquivos de áudio ou fluxos para texto.
  • A conversão de texto em fala converte o texto de entrada em fala sintetizada semelhante à humana.
  • A tradução de fala permite tradução em tempo real de vários idiomas para conversão de fala em texto e conversão de fala em fala.
  • Os assistentes de voz podem criar interfaces de conversa semelhantes a humanos para aplicativos.

Este artigo explica como a conversão de fala em texto é implementada no aplicativo de exemplo Xamarin.Forms usando o Serviço de Fala do Azure. As capturas de tela a seguir mostram o aplicativo de exemplo no iOS e no Android:

no Android Capturas de tela do aplicativo de exemplo no iOS e android

Criar um recurso do Serviço de Fala do Azure

O Serviço de Fala do Azure faz parte dos Serviços Cognitivos do Azure, que fornece APIs baseadas em nuvem para tarefas como reconhecimento de imagem, reconhecimento de fala e tradução e pesquisa do Bing. Para obter mais informações, consulte O que são os Serviços Cognitivos do Azure?.

O projeto de exemplo requer que um recurso dos Serviços Cognitivos do Azure seja criado em seu portal do Azure. Um recurso dos Serviços Cognitivos pode ser criado para um único serviço, como o Serviço de Fala ou como um recurso de vários serviços. As etapas para criar um recurso do Serviço de Fala são as seguintes:

  1. Faça logon em seu portal do Azure.
  2. Crie um recurso de vários serviços ou de serviço único.
  3. Obtenha as informações de chave de API e região para o recurso.
  4. Atualize o arquivo Constants.cs de exemplo.

Para obter um guia passo a passo para criar um recurso, consulte Criar um recurso dos Serviços Cognitivos.

Observação

Se você não tiver uma assinatura do Azure, crie uma conta gratuita antes de começar. Depois que você tiver uma conta, um recurso de serviço único poderá ser criado na camada gratuita para experimentar o serviço.

Configurar seu aplicativo com o Serviço de Fala

Depois de criar um recurso dos Serviços Cognitivos, o arquivo Constants.cs pode ser atualizado com a região e a chave de API do recurso do Azure:

public static class Constants
{
    public static string CognitiveServicesApiKey = "YOUR_KEY_GOES_HERE";
    public static string CognitiveServicesRegion = "westus";
}

Instalar o pacote do Serviço de Fala do NuGet

O aplicativo de exemplo usa o pacote NuGet Microsoft.CognitiveServices.Speech para se conectar ao Serviço de Fala do Azure. Instale esse pacote NuGet no projeto compartilhado e em cada projeto de plataforma.

Criar uma interface IMicrophoneService

Cada plataforma requer permissão para acessar o microfone. O projeto de exemplo fornece uma IMicrophoneService interface no projeto compartilhado e usa o Xamarin.FormsDependencyService para obter implementações de plataforma da interface.

public interface IMicrophoneService
{
    Task<bool> GetPermissionAsync();
    void OnRequestPermissionResult(bool isGranted);
}

Criar o layout da página

O projeto de exemplo define um layout de página básico no arquivo MainPage.xaml . Os elementos de layout de chave são um Button que inicia o processo de transcrição, um Label para conter o texto transcrito e um ActivityIndicator para mostrar quando a transcrição está em andamento:

<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>

Implementar o Serviço de Fala

O arquivo code-behind MainPage.xaml.cs contém toda a lógica para enviar áudio e receber texto transcrito do Serviço de Fala do Azure.

O MainPage construtor obtém uma instância da IMicrophoneService interface do DependencyService:

public partial class MainPage : ContentPage
{
    SpeechRecognizer recognizer;
    IMicrophoneService micService;
    bool isTranscribing = false;

    public MainPage()
    {
        InitializeComponent();

        micService = DependencyService.Resolve<IMicrophoneService>();
    }

    // ...
}

O TranscribeClicked método é chamado quando a transcribeButton instância é tocada:

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();
}

O método TranscribeClicked faz o seguinte:

  1. Verifica se o aplicativo tem acesso ao microfone e sai mais cedo se não tem.
  2. Cria uma instância da SpeechRecognizer classe se ela ainda não existir.
  3. Interromperá a transcrição contínua se ela estiver em andamento.
  4. Insere um carimbo de data/hora e inicia a transcrição contínua se não estiver em andamento.
  5. Notifica o aplicativo para atualizar sua aparência com base no novo estado do aplicativo.

O restante dos métodos de MainPage classe são auxiliares para exibir o estado do aplicativo:

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;
        }
    });
}

O UpdateTranscription método grava o fornecido newTextstring no Label elemento chamado transcribedText. Ele força essa atualização a ocorrer no thread da interface do usuário para que ela possa ser chamada de qualquer contexto sem causar exceções. O InsertDateTimeRecord grava a data e a hora atuais na transcribedText instância para marcar o início de uma nova transcrição. Por fim, o UpdateDisplayState método atualiza os Button elementos e ActivityIndicator para refletir se a transcrição está ou não em andamento.

Criar serviços de microfone de plataforma

O aplicativo deve ter acesso ao microfone para coletar dados de fala. A IMicrophoneService interface deve ser implementada e registrada com o DependencyService em cada plataforma para que o aplicativo funcione.

Android

O projeto de exemplo define uma IMicrophoneService implementação para Android chamada 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);
            }
        }
    }
}

O AndroidMicrophoneService tem os seguintes recursos:

  1. O Dependency atributo registra a classe com o DependencyService.
  2. O GetPermissionAsync método verifica se as permissões são necessárias com base na versão do SDK do Android e chama RequestMicPermissions se a permissão ainda não foi concedida.
  3. O RequestMicPermissions método usa a Snackbar classe para solicitar permissões do usuário se uma lógica for necessária, caso contrário, ele solicitará diretamente permissões de gravação de áudio.
  4. O OnRequestPermissionResult método é chamado com um bool resultado depois que o usuário responde à solicitação de permissões.

A MainActivity classe é personalizada para atualizar a instância quando as AndroidMicrophoneService solicitações de permissões são concluídas:

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;
        }
    }
}

A MainActivity classe define uma referência estática chamada Instance, que é exigida pelo AndroidMicrophoneService objeto ao solicitar permissões. Ele substitui o OnRequestPermissionsResult método para atualizar o AndroidMicrophoneService objeto quando a solicitação de permissões é aprovada ou negada pelo usuário.

Por fim, o aplicativo Android deve incluir a permissão para gravar áudio no arquivo AndroidManifest.xml :

<manifest ...>
    ...
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest>

iOS

O projeto de exemplo define uma IMicrophoneService implementação para iOS chamada 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);
            });
        }
    }
}

O iOSMicrophoneService tem os seguintes recursos:

  1. O Dependency atributo registra a classe com o DependencyService.
  2. O GetPermissionAsync método chama RequestMicPermissions para solicitar permissões do usuário do dispositivo.
  3. O RequestMicPermissions método usa a instância compartilhada AVAudioSession para solicitar permissões de gravação.
  4. O OnRequestPermissionResult método atualiza a TaskCompletionSource instância com o valor fornecido bool .

Por fim, o aplicativo iOS Info.plist deve incluir uma mensagem informando ao usuário por que o aplicativo está solicitando acesso ao microfone. Edite o arquivo Info.plist para incluir as seguintes marcas dentro do <dict> elemento :

<plist>
    <dict>
        ...
        <key>NSMicrophoneUsageDescription</key>
        <string>Voice transcription requires microphone access</string>
    </dict>
</plist>

UWP

O projeto de exemplo define uma IMicrophoneService implementação para UWP chamada 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
        }
    }
}

O UWPMicrophoneService tem os seguintes recursos:

  1. O Dependency atributo registra a classe com o DependencyService.
  2. O GetPermissionAsync método tenta inicializar uma MediaCapture instância. Se isso falhar, ele iniciará uma solicitação de usuário para habilitar o microfone.
  3. O OnRequestPermissionResult método existe para satisfazer a interface, mas não é necessário para a implementação da UWP.

Por fim, o Package.appxmanifest UWP deve especificar que o aplicativo usa o microfone. Clique duas vezes no arquivo Package.appxmanifest e selecione a opção Microfone na guia Funcionalidades no Visual Studio 2019:

Captura de tela do manifesto no Visual Studio 2019

Testar o aplicativo

Execute o aplicativo e clique no botão Transcrever . O aplicativo deve solicitar acesso ao microfone e iniciar o processo de transcrição. O ActivityIndicator animará, mostrando que a transcrição está ativa. Enquanto você fala, o aplicativo transmitirá dados de áudio para o recurso dos Serviços de Fala do Azure, que responderá com texto transcrito. O texto transcrito será exibido no elemento conforme Label ele for recebido.

Observação

Os emuladores do Android falham ao carregar e inicializar as bibliotecas do Serviço de Fala. O teste em um dispositivo físico é recomendado para a plataforma Android.