<file_sep>﻿using System;
using Android.App;
using Android.Content.PM;
using Android.OS;
using Microsoft.Practices.Unity;
using Prism.Unity;
using Android.Preferences;
using Android.Content;

namespace VoiceRecognitionSample.Droid
{
	[Activity(Label = "VoiceRecognitionSample.Droid", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
	public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
	{
		public event EventHandler<PreferenceManager.ActivityResultEventArgs> ActivityResult = delegate {};

		protected override void OnCreate(Bundle bundle)
		{
			base.OnCreate(bundle);
			global::Xamarin.Forms.Forms.Init(this, bundle);
			LoadApplication(new App(new AndroidInitializer()));
		}

		protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
		{
			base.OnActivityResult(requestCode, resultCode, data);
			var resultEventArgs = new PreferenceManager.ActivityResultEventArgs(true, requestCode, resultCode, data);
			ActivityResult(this, resultEventArgs);
		}
	}

	public class AndroidInitializer : IPlatformInitializer
	{
		public void RegisterTypes(IUnityContainer container)
		{

		}
	}
}
<file_sep># VoiceRecognitionSample
Prism for Xamarin.Forms（Prism.Forms）で作成した、音声認識のサンプルアプリです。

## 対応プラットフォーム
- iOS（バージョン10.0以上）
- Android

## 機能
音声を認識し、画面上に認識結果の文章を表示します。

## 画面ショット

#### 音声認識実行前
|iOS|Android|
|---|---|
|![iOS](Screenshot/iOS/initial.png)|![Android](Screenshot/Android/initial.png)|

開始ボタンを押下した後に音声を吹き込むと、その音声の認識結果が画面上に表示されます。

#### 音声認識実行後
認識結果は開始/停止ボタンの真上に表示されます。

|iOS|Android|
|---|---|
|![iOS](Screenshot/iOS/after_voice_recognition.png)|![Android](Screenshot/Android/after_voice_recognition.png)|


## 注意・免責
- どちらのプラットフォームでも、音声認識を使用する際はインターネットへの接続が必須です。
- [iOSの場合、端末ごと・アプリごとの音声認識処理の使用回数等の制限があります。](http://qiita.com/nerd0geek1/items/af9f0555c3f6c8d878eb)
- 本アプリの利用は自己責任でお願いします。
<file_sep>﻿using System;
using AVFoundation;
using Foundation;
using Prism.Mvvm;
using Speech;
using VoiceRecognitionSample.Models.iOS;
using Xamarin.Forms;

[assembly: Dependency(typeof(VoiceRecognitionService))]
namespace VoiceRecognitionSample.Models.iOS
{
	/// <summary>
	/// 音声認識用サービス
	/// プロパティの変更をバインドで捉えられるようにするため、BindableBaseを継承する。
	/// </summary>
	public class VoiceRecognitionService : BindableBase, IVoiceRecognitionService
	{
		#region Properties

		/// <summary>
		/// 音声認識の実行状況（実行中の間のみtrueを返す）
		/// </summary>
		private bool _isRecognizing;
		public bool IsRecognizing
		{
			get { return _isRecognizing; }
			set { SetProperty(ref _isRecognizing, value); }
		}

		/// <summary>
		/// 音声認識の結果テキスト
		/// </summary>
		private string _recognizedText;
		public string RecognizedText
		{
			get
			{
				if (_recognizedText != null)
					return _recognizedText;
				else
					return string.Empty;
			}
			set { SetProperty(ref _recognizedText, value); }
		}

		#endregion

		#region Variables

		/// 音声認識に必要な諸々のクラスのインスタンス
		private AVAudioEngine audioEngine;
		private SFSpeechRecognizer speechRecognizer;
		private SFSpeechAudioBufferRecognitionRequest recognitionRequest;
		private SFSpeechRecognitionTask recognitionTask;

		#endregion

		#region Public Methods

		/// <summary>
		/// 音声認識の開始処理
		/// </summary>
		public void StartRecognizing()
		{
			RecognizedText = string.Empty;
			IsRecognizing = true;

			// 音声認識の許可をユーザーに求める。
			SFSpeechRecognizer.RequestAuthorization((SFSpeechRecognizerAuthorizationStatus status) =>
				{
					switch (status)
					{
						case SFSpeechRecognizerAuthorizationStatus.Authorized:
							// 音声認識がユーザーに許可された場合、必要なインスタンスを生成した後に音声認識の本処理を実行する。
							// SFSpeechRecognizerのインスタンス生成時、コンストラクタの引数でlocaleを指定しなくても、
							// 端末の標準言語が日本語なら日本語は問題なく認識される。
							audioEngine = new AVAudioEngine();
							speechRecognizer = new SFSpeechRecognizer();
							recognitionRequest = new SFSpeechAudioBufferRecognitionRequest();
							startRecognitionSession();
							break;
						default:
							// 音声認識がユーザーに許可されなかった場合、処理を終了する。
							return;
					}
				}
			);
		}

		/// <summary>
		/// 音声認識の停止処理
		/// </summary>
		public void StopRecognizing()
		{
			try
			{
				audioEngine?.Stop();
				recognitionTask?.Cancel();
				recognitionRequest?.EndAudio();
				IsRecognizing = false;
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.Message);
			}
		}

		#endregion

		#region Private Methods

		/// <summary>
		/// 音声認識の本処理
		/// </summary>
		private void startRecognitionSession()
		{
			// 音声認識のパラメータ設定と認識開始。ここのパラメータはおまじない。
			audioEngine.InputNode.InstallTapOnBus(
				bus: 0,
				bufferSize: 1024,
				format: audioEngine.InputNode.GetBusOutputFormat(0),
				tapBlock: (buffer, when) => { recognitionRequest?.Append(buffer); }
			);
			audioEngine?.Prepare();
			NSError error = null;
			audioEngine?.StartAndReturnError(out error);
			if (error != null)
			{
				Console.WriteLine(error);
				return;
			}

			try
			{
				if (recognitionTask?.State == SFSpeechRecognitionTaskState.Running)
				{
					// 音声認識が実行中に音声認識開始処理が呼び出された場合、実行中だった音声認識を中断する。
					recognitionTask.Cancel();
				}

				recognitionTask = speechRecognizer.GetRecognitionTask(recognitionRequest,
					(SFSpeechRecognitionResult result, NSError err) =>
					{
						if (result == null)
						{
							// iOS Simulator等、端末が音声認識に対応していない場合はここに入る。
							StopRecognizing();
							return;
						}
						
						if (err != null)
						{
							Console.WriteLine(err);
							StopRecognizing();
							return;
						}
						
						if ((result.BestTranscription != null) && (result.BestTranscription.FormattedString != null))
						{
							// 音声を認識できた場合、認識結果を更新する。
							RecognizedText = result.BestTranscription.FormattedString;
						}
						
						if (result.Final)
						{
							// 音声が認識されなくなって時間が経ったら音声認識を打ち切る。
							StopRecognizing();
							return;
						}
					}
				);
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.Message);
			}
		}

		#endregion
	}
}
<file_sep>﻿using System.ComponentModel;
using System.Windows.Input;
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using VoiceRecognitionSample.Models;

namespace VoiceRecognitionSample.ViewModels
{
	/// <summary>
	/// MainPage.xamlに対応するViewModel
	/// </summary>
	public class MainPageViewModel : BindableBase, INavigationAware
	{
		#region Constants

		/// <summary>
		/// 音声認識の開始・停止ボタンのテキスト
		/// </summary>
		private const string BUTTON_TEXT_START = "開始";
		private const string BUTTON_TEXT_STOP = "停止";

		#endregion

		#region Properties, Variables

		/// <summary>
		/// 音声認識の結果テキスト
		/// </summary>
		private string _recognizedText = string.Empty;
		public string RecognizedText
		{
			get { return _recognizedText; }
			protected set { SetProperty(ref _recognizedText, value); }
		}

		/// <summary>
		/// 音声認識の開始・停止ボタンの表記
		/// </summary>
		private string _voiceRecognitionButtonText = BUTTON_TEXT_START;
		public string VoiceRecognitionButtonText
		{
			get { return _voiceRecognitionButtonText; }
			protected set { SetProperty(ref _voiceRecognitionButtonText, value); }
		}

		/// <summary>
		/// 音声認識を実行中かどうか（trueなら実行中）
		/// </summary>
		private bool _isRecognizing;
		public bool IsRecognizing
		{
			get { return _isRecognizing; }
			protected set
			{
				// 音声認識が実行中の場合、音声認識ボタンのテキストを「停止」に変更する。
				// 音声認識が停止している場合は「開始」に変更する。
				VoiceRecognitionButtonText = value ? BUTTON_TEXT_STOP : BUTTON_TEXT_START;
				SetProperty(ref _isRecognizing, value);
			}
		}

		/// <summary>
		/// 音声認識サービス
		/// </summary>
		private readonly IVoiceRecognitionService _voiceRecognitionService;

		/// <summary>
		/// 音声認識サービスの処理の呼び出し用コマンド
		/// </summary>
		public ICommand VoiceRecognitionCommand { get; }

		#endregion

		#region Constructor

		/// <summary>
		/// コンストラクタ
		/// </summary>
		public MainPageViewModel(IVoiceRecognitionService voiceRecognitionService)
		{
			_voiceRecognitionService = voiceRecognitionService;

			// 音声認識サービスのプロパティが変更されたときに実行する処理を設定する。
			_voiceRecognitionService.PropertyChanged += voiceRecognitionServicePropertyChanged;

			// 音声認識サービスの処理本体をコマンドに紐付ける。
			VoiceRecognitionCommand = new DelegateCommand(executeVoiceRecognition);
		}

		#endregion

		#region Event of NavigationAware

		public void OnNavigatedFrom(NavigationParameters parameters)
		{

		}

		public void OnNavigatedTo(NavigationParameters parameters)
		{

		}

		public void OnNavigatingTo(NavigationParameters parameters)
		{
			
		}

		#endregion

		#region Private Methods

		/// <summary>
		/// 音声認識サービスのプロパティ変更時にトリガーされるイベントの実処理
		/// </summary>
		/// <param name="sender">Sender</param>
		/// <param name="args">Arguments</param>
		private void voiceRecognitionServicePropertyChanged(object sender, PropertyChangedEventArgs args)
		{
			if (args.PropertyName == "RecognizedText")
			{
				// 音声の認識結果テキストの変更がトリガーになった場合、そのテキストをViewModelに取得する。
				RecognizedText = _voiceRecognitionService.RecognizedText;
			}
			if (args.PropertyName == "IsRecognizing")
			{
				// 音声認識の実行状況変更がトリガーになった場合、その実行状況をViewModelに取得する。
				IsRecognizing = _voiceRecognitionService.IsRecognizing;
			}
		}

		/// <summary>
		/// 音声認識サービス呼び出し用ボタンのコマンドの実処理
		/// </summary>
		private void executeVoiceRecognition()
		{
			if (IsRecognizing)
			{
				// 音声認識を実行中の場合、「停止」ボタンとして機能させる。
				_voiceRecognitionService.StopRecognizing();
			}
			else
			{
				// 音声認識が停止中の場合、「開始」ボタンとして機能させる。
				_voiceRecognitionService.StartRecognizing();
			}
		}

		#endregion
	}
}

