Androidでも音声認識
先日のWWDCでSiriのAPIを公開するという発表がありました。
これでサードパーティのアプリに組み込むことができるようになったので、今後音声を利用したアプリが増えてきそうですね。
そこで、Androidでも音声を使ってみようと思います!
AndroidMから音声認識のAPIが使えるようになっているので、今回はそれを利用したサンプルアプリのご紹介です。
Voice Interaction API
https://developers.google.com/voice-actions/interaction/voice-interactions
まずは普通にプロジェクトを作成。
次にAndroidManifet.xmlに以下のようにintent-filterを作成します。
<intent-filter> <action android:name="android.media.action.STILL_IMAGE_CAMERA" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.VOICE" /> </intent-filter>
音声コマンドで立ち上がった場合は、isVoiceInteraction()にtrueが返却されるので、以下のように判定します。
@Override protected void onResume() { super.onResume(); if (isVoiceInteraction()) { startVoiceTrigger(); } }
getVoiceInteraction().submitRequestで音声確認を要求します。
ここが実際に音声のやりとりを行う所になります。
PickOptionRequest.Optionで発声する文字のリストを作成します。
(リストに含まれている文字が発呼されたらonPickOptionResultにコールバックされます)
今回は「select pet」という要求が表示され、「dog」もしくは「cat」といった場合のみトーストが表示されます。
PickOptionRequest.Option[] options = new PickOptionRequest.Option[] { new PickOptionRequest.Option("dog", 0), new PickOptionRequest.Option("cat", 1) }; getVoiceInteractor().submitRequest( new VoiceInteractor.PickOptionRequest(newVoiceInteractor.Prompt("select pet"), options, new Bundle()) { @Override public void onPickOptionResult(boolean finished, Option[] selections, Bundle result) { if (finished) { Toast.makeText(getApplicationContext(), "Ok", Toast.LENGTH_SHORT).show(); } } } );
ここまで作成したらアプリを実行。
ホーム画面で「ok google」。
そして、「take a selfie」でアプリを起動することができます。
アプリが立ち上がると、「select pet」と聞かれるので、
今回は「cat」と発声!(「dog」でも可)
すると、onPickOptionResultが呼ばれてトーストが表示されました!
以上、簡単ですがAndroidでの音声操作の利用方法でした。
音声を使用すれば、手を使わなくてもスマホを操作できるようになるので、手が使えないときの画面のスクロールや切り替えなどがしやすくなるかも?(料理中とか?)
下記URL先を見れば、手順を踏みながら作成することもできるので、興味ある方はぜひ触ってみてください。
http://io2015codelabs.appspot.com/codelabs/voice-interaction#1
ではでは~~。
【Swift】KVCを用いたJSONパーサー
REST-APIやWebSocketで通信するアプリを作っているとどうしても通らないといけないのがJSONのパース。
普通に実装するとこのようになると思います
class Object: NSObject { dynamic var str0: String = "a" dynamic var str1: String = "b" dynamic var str2: String = "c" dynamic var num0: Int = 0 dynamic var num1: Int = 1 dynamic var num2: Int = 2 init(dictionary: [String : AnyObject]) { super.init() str0 = dictionary["str0"] as! String str1 = dictionary["str1"] as! String str2 = dictionary["str2"] as! String num0 = dictionary["num0"] as! Int num1 = dictionary["num1"] as! Int num2 = dictionary["num2"] as! Int } }
しかし、APIが増えるごとにそれぞれのモデルごとにinitを書いているのは正直疲れます。 今回はKVC(Key Value Coding)を用いて、initを書かずにJSON⇔モデルクラスを実現する方法をSwiftで紹介します。
KVCとは?
キー値コーディングとは、オブジェクトのプロパティに間接的にアクセスするための仕組みです。ア クセサメソッドを呼び出してアクセスしたり、インスタンス変数としてアクセスするのではなく、プ ロパティの識別に文字列を使用してアクセスします。 引用元: https://developer.apple.com/jp/documentation/KeyValueCoding.pdf
要は、プロパティに対して.str1のようにアクセサメソッドやインスタンス変数でアクセスするのではなく、 "str1"といった文字列でプロパティに対してアクセスする手法です。 Objective-CではこのKVCを使用してプライベートなAPIを無理やり叩くこともできました。(Swiftではやったことないけど...)
プロパティ名の取得
オブジェクトからJSONにマッピングするためにはKeyとなる物が必要なので、 JSONのKeyに対応するプロパティ名をすべて取得します。
class KVCModel: NSObject { class func allKeys() -> [String] { var names: [String] = [] var count: UInt32 = 0 self.classForCoder() let properties:UnsafeMutablePointer<objc_property_t> = class_copyPropertyList(self.classForCoder(), &count) for i: UInt32 in 0..<count { let property = properties[Int(i)]; let cname = property_getName(property) let name = String.fromCString(cname) names.append(name!) } free(properties) return names } }
self.classForCoder()
では自身のクラスを取得します。
Objective-Cでいうところの
+ (Class)class;
に対応しています。
イニシャライザでマッピングする JSON->モデル
イニシャライザにJSONから取り出したDictionaryを渡すことで自動的にプロパティに値が設定されるようにします。
class KVCModel: NSObject { init(dictionary: [String : AnyObject]) { super.init() if dictionary.count == 0 { return } let allPropertyKeys = self.dynamicType.allKeys() for key: String in allPropertyKeys { let val = dictionary[key] if val != nil { self.setValue(val, forKey: key) } } } }
self.dynamicType
でも自身のクラスを取得していますが、 こちらはObjective-Cでは
- (Class)class;
に対応しています。
Dictionaryで取り出す モデル->JSON
最後に以下のメソッドを追加し、モデルのプロパティをDictionaryとして取り出せるようにします。
func dictionaryObject() -> [String : AnyObject] { let allKeys = self.dynamicType.allKeys() return self.dictionaryWithValuesForKeys(allKeys) }
動作
以上までで作成したクラスを継承し、モデルクラスを作成しましょう。
class Object: KVCModel { dynamic var str0: String = "" dynamic var str1: String = "" dynamic var str2: String = "" dynamic var num0: Int = 0 dynamic var num1: Int = 0 dynamic var num2: Int = 0 }
テストコード
let dic = ["str1" : "str", "str2" : "strstr", "num1": 1, "num2" : 2] var jsonData: NSData = NSData() var jsonDic: [String : AnyObject] = [:] // Dictionary => JSON do { jsonData = try NSJSONSerialization.dataWithJSONObject(dic, options: .PrettyPrinted) } catch { // なんかやる } // JSON => Dictionary do { jsonDic = try NSJSONSerialization.JSONObjectWithData(jsonData, options: .AllowFragments) as! Dictionary } catch { // なんかやる } // Dictionary => Model let model: Model = Model(dictionary: jsonDic) print("str1 : \(model.str1)") print("str2 : \(model.str2)") print("num1 : \(model.num1)") print("num2 : \(model.num2)") print("dic : \(model.dictionaryObject())")
ログ
str1 : str str2 : strstr num1 : 1 num2 : 2 dic : ["num2": 2, "str2": strstr, "num1": 1, "str1": str]