Azure 음성 서비스를 사용한 음성 인식

Download Sample 샘플 다운로드

Azure Speech Service는 다음과 같은 기능을 제공하는 클라우드 기반 API입니다.

  • 음성 텍스트 변환 은 오디오 파일 또는 스트림을 텍스트로 전사합니다.
  • 텍스트 음성 변환은 입력 텍스트를 인간과 유사한 합성된 음성으로 변환합니다.
  • 음성 번역 을 사용하면 음성 텍스트 변환과 음성 음성 변환 모두에 대해 실시간 다중 언어 번역을 사용할 수 있습니다.
  • 음성 도우미 애플리케이션에 대한 인간과 유사한 대화 인터페이스를 만들 수 있습니다.

이 문서에서는 Azure Speech Service를 사용하여 샘플 Xamarin.Forms 애플리케이션에서 음성 텍스트 변환을 구현하는 방법을 설명합니다. 다음 스크린샷은 iOS 및 Android의 샘플 애플리케이션을 보여줍니다.

Screenshots of the sample application on iOS and Android

Azure Speech Service 리소스 만들기

Azure Speech Service는 이미지 인식, 음성 인식 및 번역, Bing 검색과 같은 작업에 클라우드 기반 API를 제공하는 Azure Cognitive Services의 일부입니다. 자세한 내용은 Azure Cognitive Services란?을 참조하세요.

샘플 프로젝트에는 Azure Portal에서 Azure Cognitive Services 리소스를 만들어야 합니다. Speech Service와 같은 단일 서비스 또는 다중 서비스 리소스로 Cognitive Services 리소스를 만들 수 있습니다. Speech Service 리소스를 만드는 단계는 다음과 같습니다.

  1. Azure Portal에 로그인합니다.
  2. 다중 서비스 또는 단일 서비스 리소스를 만듭니다.
  3. 리소스에 대한 API 키 및 지역 정보를 가져옵니다.
  4. 샘플 Constants.cs 파일을 업데이트합니다.

리소스를 만드는 단계별 가이드는 Cognitive Services 리소스 만들기를 참조하세요.

참고 항목

Azure 구독이 아직 없는 경우 시작하기 전에 체험 계정을 만듭니다. 계정이 있으면 무료 계층에서 단일 서비스 리소스를 만들어 서비스를 사용해 볼 수 있습니다.

Speech Service를 사용하여 앱 구성

Cognitive Services 리소스 를 만든 후 Constants.cs 파일을 Azure 리소스의 지역 및 API 키로 업데이트할 수 있습니다.

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

NuGet Speech Service 패키지 설치

샘플 애플리케이션은 Microsoft.CognitiveServices.Speech NuGet 패키지를 사용하여 Azure Speech Service에 연결합니다. 공유 프로젝트 및 각 플랫폼 프로젝트에 이 NuGet 패키지를 설치합니다.

IMicrophoneService 인터페이스 만들기

각 플랫폼에는 마이크에 액세스할 수 있는 권한이 필요합니다. 샘플 프로젝트는 공유 프로젝트의 인터페이스를 IMicrophoneService 제공하고 이를 사용하여 Xamarin.FormsDependencyService 인터페이스의 플랫폼 구현을 가져옵니다.

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

페이지 레이아웃 만들기

샘플 프로젝트는 MainPage.xaml 파일에서 기본 페이지 레이아웃을 정의합니다. 키 레이아웃 요소는 Button 전사 프로세스를 시작하고, Label 전사된 텍스트를 포함하며 ActivityIndicator , 전사가 진행 중일 때 표시하는 요소입니다.

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

Speech Service 구현

MainPage.xaml.cs 코드 숨김 파일에는 오디오를 보내고 Azure Speech Service에서 전사된 텍스트를 수신하는 모든 논리가 포함되어 있습니다.

MainPage 생성자는 다음에서 인터페이스의 IMicrophoneService 인스턴스를 DependencyService가져옵니다.

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

    public MainPage()
    {
        InitializeComponent();

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

    // ...
}

TranscribeClicked 인스턴스를 탭할 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();
}

메서드는 TranscribeClicked 다음을 수행합니다.

  1. 애플리케이션이 마이크에 액세스할 수 있는지 확인하고, 그렇지 않으면 일찍 종료합니다.
  2. 클래스 인스턴스 SpeechRecognizer 가 아직 없는 경우 만듭니다.
  3. 진행 중인 경우 연속 전사를 중지합니다.
  4. 타임스탬프를 삽입하고 진행 중이 아닌 경우 연속 전사를 시작합니다.
  5. 애플리케이션에 새 애플리케이션 상태에 따라 모양을 업데이트하도록 알 수 있습니다.

클래스 메서드의 MainPage re기본der는 애플리케이션 상태를 표시하기 위한 도우미입니다.

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

메서드는 UpdateTranscription 제공된 요소를 명명transcribedTextnewTextstring 요소에 Label 씁니다. 이 업데이트는 UI 스레드에서 강제로 발생하므로 예외를 일으키지 않고 컨텍스트에서 호출할 수 있습니다. 새 InsertDateTimeRecord 전사의 시작을 표시하기 위해 transcribedText 인스턴스에 현재 날짜 및 시간을 씁니다. 마지막으로, 메서드는 UpdateDisplayState 전사가 Button 진행 중인지 여부를 반영하도록 요소 및 ActivityIndicator 요소를 업데이트합니다.

플랫폼 마이크 서비스 만들기

음성 데이터를 수집하려면 애플리케이션에 마이크 액세스 권한이 있어야 합니다. 애플리케이션이 IMicrophoneService 작동하려면 인터페이스를 구현하고 각 플랫폼에 등록 DependencyService 해야 합니다.

Android

샘플 프로젝트는 다음이라는 AndroidMicrophoneServiceAndroid 구현을 IMicrophoneService 정의합니다.

[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 같은 기능이 있습니다.

  1. 이 특성은 Dependency 클래스 DependencyService를 .에 등록합니다.
  2. 이 메서드는 GetPermissionAsync Android SDK 버전에 따라 사용 권한이 필요한지 검사 권한이 아직 부여되지 않은 경우 호출 RequestMicPermissions 합니다.
  3. 이 메서드는 RequestMicPermissions 근거가 Snackbar 필요한 경우 클래스를 사용하여 사용자에게 권한을 요청합니다. 그렇지 않으면 오디오 녹음 권한을 직접 요청합니다.
  4. OnRequestPermissionResult 사용자가 권한 요청에 응답하면 메서드가 결과와 함께 bool 호출됩니다.

MainActivity 사용 권한 요청이 완료되면 인스턴스를 AndroidMicrophoneService 업데이트하도록 클래스가 사용자 지정됩니다.

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

클래스는 MainActivity 사용 권한을 요청할 때 개체에 AndroidMicrophoneService 필요한 정적 참조를 Instance정의합니다. 사용자가 사용 권한 요청을 승인하거나 거부할 때 개체를 업데이트 AndroidMicrophoneService 하는 메서드를 재정 OnRequestPermissionsResult 의합니다.

마지막으로 Android 애플리케이션은 AndroidManifest.xml 파일에 오디오를 녹음할 수 있는 권한을 포함해야 합니다.

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

iOS

샘플 프로젝트는 다음과 같은 iOSMicrophoneServiceiOS 구현을 IMicrophoneService 정의합니다.

[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 같은 기능이 있습니다.

  1. 이 특성은 Dependency 클래스 DependencyService를 .에 등록합니다.
  2. 메서드는 GetPermissionAsync 디바이스 사용자의 권한을 요청하기 위해 호출 RequestMicPermissions 합니다.
  3. 이 메서드는 RequestMicPermissions 공유 AVAudioSession 인스턴스를 사용하여 기록 권한을 요청합니다.
  4. 메서드는 OnRequestPermissionResult 제공된 bool 값으로 인스턴스를 업데이트합니다TaskCompletionSource.

마지막으로 iOS 앱 Info.plist 에는 앱이 마이크에 대한 액세스를 요청하는 이유를 사용자에게 알리는 메시지가 포함되어야 합니다. Info.plist 파일을 편집하여 요소 내에 다음 태그를 <dict> 포함합니다.

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

UWP

샘플 프로젝트는 다음이라는 UWPMicrophoneServiceUWP에 대한 구현을 정의합니다IMicrophoneService.

[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 같은 기능이 있습니다.

  1. 이 특성은 Dependency 클래스 DependencyService를 .에 등록합니다.
  2. 메서드는 GetPermissionAsync 인스턴스를 초기화하려고 시도합니다 MediaCapture . 실패하면 마이크를 사용하도록 설정하는 사용자 요청을 시작합니다.
  3. 이 메서드는 OnRequestPermissionResult 인터페이스를 충족하기 위해 존재하지만 UWP 구현에는 필요하지 않습니다.

마지막으로 UWP Package.appxmanifest 는 애플리케이션이 마이크를 사용한다고 지정해야 합니다. Package.appxmanifest 파일을 두 번 클릭하고 Visual Studio 2019의 기능 탭에서 마이크 옵션을 선택합니다.

Screenshot of the manifest in Visual Studio 2019

애플리케이션 테스트

앱을 실행하고 전사 단추를 클릭합니다. 앱은 마이크 액세스를 요청하고 전사 프로세스를 시작해야 합니다. 전사가 ActivityIndicator 활성 상태임을 보여 주는 애니메이션 효과입니다. 말하는 대로 앱은 기록된 텍스트로 응답하는 Azure Speech Services 리소스로 오디오 데이터를 스트리밍합니다. 전사된 텍스트는 수신될 때 요소에 Label 표시됩니다.

참고 항목

Android 에뮬레이터는 Speech Service 라이브러리를 로드하고 초기화하지 못합니다. Android 플랫폼에는 물리적 디바이스에서 테스트하는 것이 좋습니다.