четверг, 15 ноября 2012 г.

custom Voice Command System

How to customize Your Voice Commands with JavaScript and Tasker.


Script Language: JavaScript

Reference: JavaScript primer (textbook)

Structure.

The System based on Object's inheritance.


An atomic individual is object Command (vc). It has property Keywords (keyword) for Action trigger. Property Keywords set as RegExp pattern and consist of words which should be in Input String.
Second level of VCS is voice command Set object (vcSet). When initialized it has object Command as base. The functions Find, Default Action, property vcsKeywords and set of Commands are added to the base object. vcsKeywords consist of keywords of all commands in Set.




Third level is System object (vcSys). It's the same as vcSet but its vcs consist of some vcSet and vc. Actually, vcSet can contain nested vcSet too. vcSys engaged to make things clear.


Each object in VCS has two provided interfaces: membership checking (check) and custom action (action) and one required interface - set Keyword. The rest methods and properties shouldn't be called outside those objects.
The Keywords must be unique on each level of hierarchy. Else the first found object of ones with same keywords will work, only.

How it works.

App Tasker  used as system integrator.
As voice recognition tool used Google Voice Search.

Tasker calls GVS. GVS returns array of text alternatives. Tasker call VCS script with comma-delimited alternatives as input parameter.
Object vcSys created at script start. Input string checked for system's keyword. If it present, system extended with some vcSet and/or vc and system's action run.
Typical system action flow:
1. Input string checked for vcs keywords. If they are absent, system Default Action called and script finished.
2. Else vcSet or vc with keyword found and its action called
3. If found object's class is Command, its action called and script finished, else (it's voice command Set) flow returns to stage 1 and go with that vcSet.

How to make custom VCS

1. Define some Commands for Sets or System.
2. Define some voice command Sets including Commands into Set with calling they constructors in vcs. You can define Commands while defining Set as well. Join commands' keywords in vcsKeywords property.
3. Define function for calling from Tasker.

Bottlenecks.

1. Google Voice Search requires wide-band Internet connection for fast stable work.
2. It works bad in noisy environment. As workaround You can increase number of returned text alternatives. It insures that real keyword would present in one of them. As addition You can set Commands' and Sets' keywords with alternatives as well: "start|go|begin", etc. During debugging or training of VCS that'll show which words/word combinations recognized better.



// supplied AS IS
// For backtrack and comments: v.ozzzzzzz@gmail.com
// voice command core

function vc(keyword){
return {
keyword:keyword,
check: function(v){ re = new RegExp(this.keyword, 'i'); return re.test(v) },
action: function(){flash(this.keyword) }
}
}

function vcSet(keyword){
var set = vc(keyword)
// override inherited command's action
set.action = function(v){
if(set.checkSet(v)){
set.find(v)
}else{
set.defaultAction(v)
}
}
// add set's methods
set.find = function(v){
for(vc in set.vcs){
if(set.vcs[vc].check(v)){
set.vcs[vc].action(v)
break // comment it for batch commands
}
}
}
set.checkSet = function(v){ re = new RegExp(set.vcsKeywords, 'i'); return re.test(v) }
set.defaultAction = function(v){flash(set.keyword+': What? '+v)}
// add property
set.vcsKeywords = 'dummy'
// the Set
set.vcs = {}
return set
}

function vcSys(keyword){
return vcSet(keyword)
}

function sayit(s,e){
var engines = {en:'eng-USA', ru:'rus-RUS'}
e = e || 'ru'
say(s, 'com.svox.classic', engines[e], 'system', 5, 5)
}
// voice command core


function test(){
voiceCommandSystem(global('vcMap')+' command ')
}

function voiceCommandSystem(c){
try{
sys = vcSys('command')
if(sys.check(c)){
sys.vcs = {setBP:BP(), setVR:VR(), setMap:Map()}
sys.vcsKeywords = sys.vcs.setBP.keyword+'|'+sys.vcs.setVR.keyword+'|'+sys.vcs.setMap.keyword
sys.action(c)
}
}catch(e){
popup('Exception',e.name+' '+e.message,true,'','',30);
}
}

function BP(){
var set = vcSet(global('vcBP'))
set.vcs = {c1:vc(global('vcBP1')), c2:vc(global('vcBP2'))}
set.vcsKeywords = set.vcs.c1.keyword+'|'+set.vcs.c2.keyword
set.vcs.c1.action = function(){performTask('m BeyondPod', 9, 'play')}
set.vcs.c2.action = function(){performTask('m BeyondPod', 9, 'pause')}
set.defaultAction = function(v){sayit('WTF BeyondPod','en')}
return set
}

function VR(){
var set = vcSet(global('vcVR'))
set.vcs = {c1:vc(global('vcVR1')), c2:vc(global('vcVR2'))}
set.vcsKeywords = set.vcs.c1.keyword+'|'+set.vcs.c2.keyword
set.vcs.c1.action = function(){shell('am broadcast -a rubberbigpepper.VideoReg.StartRecord && sleep 3 && renice 10 -p `pidof rubberbigpepper.VideoRegPro` && renice -12 -p `pidof mediaserver`', true, 20)}
set.vcs.c2.action = function(){shell('am broadcast -a rubberbigpepper.VideoReg.StopRecord', true, 20)}
set.defaultAction = function(v){sayit('WTF Video Reg','en')}
return set
}


function Map(){
var set = vc(global('vcMap'))
set.action = function(){
sendIntent( 'android.intent.action.VIEW' , 'activity', 'ru.yandex.yandexmaps' , '',
'browsable', 'geo:'+global('LOC')+'?z='+global('ZoomHigh')+'&q=(V.Oz.)' )}
return set
}