24/7 twenty-four seven

iOS/OS X application programing topics.

iOS 5.1 の音声入力を使ってアプリケーションを操作してみる

iOS 5.1 から日本語の音声入力がサポートされました。さらに API にも UIDictationPhrase など音声認識ができそうなクラスが追加されています。
これはプログラムから音声認識を使うことができるのか?そうすると音声を使ってハンズフリーでアプリケーションの操作ができるかと思いましたが調べてみるとそんなにうまい話ではありませんでした。


まず iOS 5.1 で追加された関係のありそうな API を見てみましょう。

UITextInput.h
  • Added UIDictationPhrase
  • Added UIDictationPhrase.alternativeInterpretations
  • Added UIDictationPhrase.text
  • Added -[UITextInput dictationRecognitionFailed]
  • Added -[UITextInput dictationRecordingDidEnd]
  • Added -[UITextInput insertDictationResult:]


UIDictationPhrase といかにも便利そうな名前のクラスですが、リファレンスをよく読んでみると text プロパティと alternativeInterpretations プロパティ以外はメソッドもなく、単に UITextInput protocol の insertDictationResult: の引数に結果を詰めるだけのクラスということがわかります。

さらに読み進めていくと追加された API はすべて UITextInput protocol のメソッドであり要するに音声入力はあくまでもテキスト入力の一環で、 UITextInput protocol を通して入力を受け取ることしかできないということがわかります。

つまり手順としては

  1. UITextInput protocol を実装して
  2. ファーストレスポンダになりキーボードを出して
  3. キーボードの音声入力ボタンで録音を開始して
  4. 完了ボタンで録音した音声をサーバに送り
  5. 結果を UITextInput protocol のメソッドを通して受け取る

ということになります。


キーボードは自動で表示するとしても、入力の開始と終了でキーボードのボタンを手で押す必要があるというのは今回の目的においてはあまり便利ではありません。

そこでどうにか自動的に入力を開始/終了することができないか調べてみたところ、UIDictationController というクラスにそれっぽいメソッドがありました。

@interface UIDictationController
〜(略)〜
+ (id)sharedInstance;
+ (id)activeInstance;
- (void)stopDictation;
- (void)cancelDictation;
- (void)startDictation;
- (void)startRecordingLimitTimer;
- (void)cancelRecordingLimitTimer;
〜(略)〜
@end

実験してみると、startDictation, stopDictation, cancelDictation メソッドでそれぞれ、開始・終了・キャンセルを実行できることがわかりましたので今回はこれを利用することにしました。
公開されていないクラスのため、このままでは AppStore に申請することはできませんが、今回は実験ということで。


ということでだいたい実現できそうな気になってきたので以下のように実装してみました。
まず、なにはなくとも UITextInput protocol を実装します。 UITextInput protocol は UIKeyInput protocol を継承しているのでそちらのメソッドも実装します。
カスタムのテキストビューを作るためにテキスト入力システムとやりとりするものなので実装しなければいけないメソッドがかなりたくさんありますが、今回はテキスト入力をまじめにハンドリングする必要はないので、ほとんど空の実装でオーケーです。

@interface VNTextInputView : UIView<UITextInput, UIGestureRecognizerDelegate>

@end

#pragma mark UIKeyInput methods

- (void)deleteBackward {
    
}

〜(略)〜

#pragma mark UITextInput methods

- (NSString *)textInRange:(UITextRange *)range {
    return nil;
}

〜(略)〜

#pragma mark -

- (void)insertDictationResult:(NSArray *)dictationResult {
    [[NSNotificationCenter defaultCenter] postNotificationName:VNDictationRecognitionSucceededNotification
                                                        object:self 
                                                      userInfo:[NSDictionary dictionaryWithObject:dictationResult forKey:VNDictationResultKey]];
}

- (void)dictationRecordingDidEnd {
    [[NSNotificationCenter defaultCenter] postNotificationName:VNDictationRecordingDidEndNotification object:self];
}

- (void)dictationRecognitionFailed {
    [[NSNotificationCenter defaultCenter] postNotificationName:VNDictationRecognitionFailedNotification object:self];
}

@end


iOS 5.1 で追加された3つのメソッドで音声入力をハンドリングします。
音声入力ボタンを押す、あるいは startDictation メソッドで開始したあと、完了ボタン、あるいは stopDictation メソッドが呼ばれたタイミングで dictationRecordingDidEnd が呼ばれます。
そして自動的にサーバに録音された音声データが送られます。
サーバの処理が完了した段階で、何らかのテキストが認識された場合は insertDictationResult: が、音声認識ができなかった場合は dictationRecognitionFailed のいずれかが呼ばれます。

結果は文字列として UIDictationPhrase の text プロパティに格納されており、1つの音声データに対して複数の候補がある場合は alternativeInterpretations プロパティに配列で格納されて受け取れます。UIDictationPhrase はある程度のまとまりごとに配列になっています。


これで任意のタイミングで音声入力を利用できるようになりました。
認識の精度はかなり高いのでパターンマッチを工夫するだけでも実用になりそうです。
使い道としてはハンズフリーでもアプリケーションの操作や、別のプログラムを呼び出したり、家電のリモコンとして使ってもおもしろそうです。


ここまでを簡単な形に整理して GitHub で公開しています。
ぜひ試して改良してみてください。

kishikawakatsumi/VoiceNavigation · GitHub