使用 Azure 語音服務進行語音辨識
Azure 語音服務是雲端式 API,可提供下列功能:
- 語音轉換文字 會轉譯音訊檔案或串流到文字。
- 文字到語音 轉換會將輸入文字轉換成類似人為的合成語音。
- 語音翻譯 可為語音轉換文字和語音轉換語音啟用即時、多語言翻譯。
- 語音助理 可以為應用程式建立類似人類的對話介面。
本文說明如何使用 Azure 語音服務,在範例 Xamarin.Forms 應用程式中實作語音轉換文字。 下列螢幕快照顯示 iOS 和 Android 上的範例應用程式:
建立 Azure 語音服務資源
Azure 語音服務是 Azure 認知服務的一部分,可為影像辨識、語音辨識、語音辨識和翻譯和 Bing 搜尋等工作提供雲端式 API。 如需詳細資訊,請參閱 什麼是 Azure 認知服務?。
範例專案需要在您的 Azure 入口網站 中建立 Azure 認知服務資源。 您可以為單一服務建立認知服務資源,例如語音服務或多重服務資源。 建立語音服務資源的步驟如下:
- 登入您的 Azure 入口網站。
- 建立多服務或單一服務資源。
- 取得資源的 API 金鑰和區域資訊。
- 更新範例 Constants.cs 檔案。
如需建立資源的逐步指南,請參閱 建立認知服務資源。
注意
如果您沒有 Azure 訂用帳戶,請在開始前建立免費帳戶。 擁有帳戶之後,即可在免費層建立單一服務資源,以試用服務。
使用語音服務設定您的應用程式
建立認知服務資源之後, Constants.cs 檔案可以使用來自 Azure 資源的區域和 API 金鑰來更新:
public static class Constants
{
public static string CognitiveServicesApiKey = "YOUR_KEY_GOES_HERE";
public static string CognitiveServicesRegion = "westus";
}
安裝 NuGet 語音服務套件
範例應用程式會使用 Microsoft.CognitiveServices.Speech NuGet 套件來連線到 Azure 語音服務。 在共享專案和每個平台專案中安裝此 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>
實作語音服務
MainPage.xaml.cs程式代碼後置檔案包含從 Azure 語音服務傳送音訊和接收轉譯文字的所有邏輯。
建MainPage
構函式會從 DependencyService
取得介面的IMicrophoneService
實例:
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
方法會執行下列動作:
- 檢查應用程式是否能夠存取麥克風,並在未存取時提早結束。
- 如果類別尚未存在,請建立 類別的
SpeechRecognizer
實例。 - 如果進行中,則會停止連續轉譯。
- 插入時間戳,並在未進行時開始連續轉譯。
- 通知應用程式根據新的應用程式狀態更新其外觀。
類別方法的 MainPage
其餘部分是顯示應用程式狀態的協助程式:
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
會將 提供的 newText
string
寫入名為transcribedText
的專案Label
。 它會強制在 UI 線程上執行此更新,以便從任何內容呼叫它,而不會造成例外狀況。 會將 InsertDateTimeRecord
目前的日期和時間寫入 實例, transcribedText
以標記新轉譯的開頭。 最後, UpdateDisplayState
方法會更新 Button
和 ActivityIndicator
元素,以反映是否正在進行轉譯。
建立平台麥克風服務
應用程式必須具有麥克風存取權,才能收集語音數據。 IMicrophoneService
介面必須在每個平台上實作並註冊DependencyService
,應用程式才能運作。
Android
範例專案會 IMicrophoneService
定義 Android 的實作,稱為 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
具有下列功能:
- 屬性
Dependency
會向DependencyService
註冊 類別。 GetPermissionAsync
方法會根據 Android SDK 版本檢查是否需要許可權,如果尚未授與許可權,則會呼叫RequestMicPermissions
。- 如果需要理由,此方法
RequestMicPermissions
會Snackbar
使用 類別向使用者要求許可權,否則會直接要求音頻錄製許可權。 - 一旦使用者回應許可權要求,就會
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
會定義稱為 Instance
的靜態參考,這個參考在要求許可權時,物件需要 AndroidMicrophoneService
這個參考。 它會覆寫 OnRequestPermissionsResult
方法,以在使用者核准或拒絕許可權要求時更新 AndroidMicrophoneService
物件。
最後,Android 應用程式必須包含在AndroidManifest.xml檔案中錄製音訊的許可權:
<manifest ...>
...
<uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest>
iOS
範例項目會 IMicrophoneService
定義名為 iOSMicrophoneService
的 iOS 實作:
[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
具有下列功能:
- 屬性
Dependency
會向DependencyService
註冊 類別。 - 方法
GetPermissionAsync
會呼叫RequestMicPermissions
以向裝置使用者要求許可權。 RequestMicPermissions
方法會使用共用AVAudioSession
實例來要求錄製許可權。- 方法會
OnRequestPermissionResult
使用提供的bool
值來更新TaskCompletionSource
實例。
最後,iOS 應用程式 Info.plist 必須包含訊息,告知使用者應用程式為何要求存取麥克風。 編輯 Info.plist 檔案,以在 元素中包含 <dict>
下列標籤:
<plist>
<dict>
...
<key>NSMicrophoneUsageDescription</key>
<string>Voice transcription requires microphone access</string>
</dict>
</plist>
UWP
範例項目會 IMicrophoneService
定義名為 UWPMicrophoneService
的 UWP 實作:
[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
具有下列功能:
- 屬性
Dependency
會向DependencyService
註冊 類別。 - 方法
GetPermissionAsync
會嘗試初始化MediaCapture
實例。 如果失敗,它會啟動使用者要求來啟用麥克風。 OnRequestPermissionResult
方法的存在是為了滿足 介面,但 UWP 實作並非必要。
最後,UWP Package.appxmanifest 必須指定應用程式使用麥克風。 按兩下 Package.appxmanifest 檔案,然後選取 Visual Studio 2019 中 [功能] 索引卷標上的 [麥克風] 選項:
測試應用程式
執行應用程式,然後按兩下 [ 轉譯] 按鈕。 應用程式應該要求麥克風存取,並開始轉譯程式。 會 ActivityIndicator
以動畫顯示轉譯為使用中狀態。 當您說話時,應用程式會將音訊數據串流至 Azure 語音服務資源,以轉譯的文字回應。 謄寫的文字會在收到時出現在 Label
專案中。
注意
Android 模擬器無法載入和初始化語音服務連結庫。 建議針對Android平臺在實體裝置上進行測試。