用Kotlin开发android平台语音识别语义理解应用

Kotlin开发android平台语音识别,语义理解应用(olamisdk

转载请注明CSDN博文地址:http://blog.csdn.net/ls0609/article/details/75084994

本文使用Kotlin开发Android平台的一个语音识别方面的应用,用的是欧拉密开放平台olamisdk。

1.Kotlin简介

Kotlin是由JetBrains创建的基于JVM的编程语言,IntelliJ正是JetBrains的杰作,而android Studio是

基于IntelliJ修改而来的。Kotlin是一门包含很多函数式编程思想的面向对象编程语言。

后来了解到Kotlin原来是以一个岛的名字命名的(Котлин),它是一门静态类型编程语言,支持JVM平台,android平台,浏览器js运行环境,本地机器码等。支持与Java,Android 100%完全互操作。Kotlin生来就是为了弥补Java缺失的现代语言的特性,并极大的简化了代码,使得开发者可以编写尽量少的样板代码。

2.Kotlin,java,Swift简单比较

1.输出Hello,World!

JAVA:System.out.println("Hello,World!");

Kotlin:println("Hello,World!")

Swift:print("Hello,World!")

2.变量和常量

Java:intmVariable =10;

mVariable =20;

staticfinalintmConstant =10;

Kotlin:varmVariable =10

mVariable =20

val mConstant =10

Swift:varmVariable =10

mVariable =20

letmConstant =10

感觉Swift和Kotlin比Java简洁,Kotlin和swift很像。

3.强制类型转换

Swift:

letlabel ="Hello world "

letwidth =80

letwidthLabel = label + String(width)

Kotlin:

vallabel ="Hello world"

valwidth =80

valwidthLabel =label + width

4数组

Swift:

vartempList = ["one","two","three"]

tempList[1] ="zero"

Kotlin:

valtempList = arrayOf("one","two","three")

tempList[1] ="zero"

5.函数

Swift:func greet(_ name:String,_

day:String)->String{

return"Hello

\(name),today is \(day)."}

greet("Bob","Tuesday")

Kotlin:

fun greet(name:String, day:String):String{

return"Hello

$name, today is $day."}

greet("Bob","Tuesday")

6.类声明及用法

Swift:

声明:classShape{

var numberOfSides =0

func simpleDescription()-> String {

return"A shape

with \(numberOfSides) sides."

}

}

用法:varshape = Shape()

shape.numberOfSides =7

var shapeDescription =shape.simpleDescription()

Kotlin:

声明:classShape{

var numberOfSides =0

fun simpleDescription() ="A shape

with $numberOfSides sides."

}

用法:var shape = Shape()

shape.numberOfSides =7

var shapeDescription= shape.simpleDescription()

可见,Kotlin和Swift好像,现代语言的特征,比java这样的高级语言更加简化,更贴近自然语言

3.开发环境

本文使用的是android studio2.0版本,启动androd studio。

如下图在configure下拉菜单中选择plugins,在搜索框中搜索Kotlin,找到结果列表中的”Kotlin”插件。


如下图,找了一张还没有安装kotlin插件的图


点击右侧intall,安装后重启studio.

4.新建android项目

你可以像以前使用android stuio一样新建一个andoid项目,建立一个activity。本文用已经完成的一个demo来做示范。

如下图是一个stuio的demo工程


选择MainActivity和MessageConst两个java文件,然后选择导航栏上的code,在下拉菜单中选择convertJavafile to kotlin file


系统会自动进行转化,转化完后会生成对应的MainActivity.kt MessageConst.kt文件,打开MainActivity.kt,编译器上方会提示”Kotlin not configured”,点击一下Configure按钮,IDE就会自动帮我们配置好了!

将两个kt文件复制到src/kotlin目录下,如下图


转化后的文件,也许有些语法错误,需要按照kotlin的语法修改。

环境配置好后,来看下gradle更新有哪些区别

project的gradle代码如下:

buildscript {

ext.kotlin_version ='1.1.3-2'

repositories {

jcenter()

}

dependencies {

classpath'com.android.tools.build:gradle:2.0.0'

//此处多了kotlin插件依赖

classpath"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

}

}

allprojects {

repositories {

jcenter()

}

}

再来看看某个module的gradle代码:

apply plugin:'com.android.application'

apply plugin:'kotlin-android'//此处多了这条插件声明

android {

compileSdkVersion14

buildToolsVersion"24.0.0"

defaultConfig {

applicationId"com.olami"

minSdkVersion8

targetSdkVersion14

}

buildTypes {

release {

minifyEnabledfalse

proguardFilesgetDefaultProguardFile('proguard-android.txt'),'proguard-rules.txt'

}

}

sourceSets {

main.java.srcDirs +='src/main/kotlin'//生成的***.kt文件需要copy到对应的目录

}

}

dependencies {

compile'com.android.support:support-v4:18.0.0'

compile files('libs/voicesdk_android.jar')

compile"org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"//此处多了kotlin包的依赖

}

repositories {

mavenCentral()

}

如上所示,如果不是通过转化的方式新建kotlin工程,则需要自己按照上面的gradle中增加的部分配置好。

5.olami语音识别应用

先贴一张识别后的效果图:


在MainActivity.kt中

override funonCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

initHandler()//初始化handler用于处理消息

initView()//初始化view控件,比如点击开始录音的button

initViaVoiceRecognizerListener()//初始化语音识别回调,用于返回录音状态和识别结果

init()//初始化语音识别对象

}

fun init()

{

initHandler()

//定义olami语音识别对象

mOlamiVoiceRecognizer =OlamiVoiceRecognizer(this@MainActivity)

valtelephonyManager =this.getSystemService(

Context.TELEPHONY_SERVICE) as TelephonyManager

valimei = telephonyManager.deviceId

mOlamiVoiceRecognizer!!.init(imei)

//set null if you do not want to notify

olami server.

//设置回调,用于更新录音状态和数据等的界面

mOlamiVoiceRecognizer!!.setListener(mOlamiVoiceRecognizerListener)

//设置支持的语言类型,默认请设置简体中文

mOlamiVoiceRecognizer!!.setLocalization(

OlamiVoiceRecognizer.LANGUAGE_SIMPLIFIED_CHINESE)

mOlamiVoiceRecognizer!!.setAuthorization("51a4bb56ba954655a4fc834bfdc46af1",

"asr","68bff251789b426896e70e888f919a6d","nli")

//注册Appkey,在olami官网注册应用后生成的appkey

//注册api,请直接填写“asr”,标识语音识别类型

//注册secret,在olami官网注册应用后生成的secret

mOlamiVoiceRecognizer!!.setVADTailTimeout(2000)

//录音时尾音结束时间,建议填//2000ms

mOlamiVoiceRecognizer!!.setLatitudeAndLongitude(

31.155364678184498,121.34882432933009)

//设置经纬度信息,不愿上传位置信息,可以填0

}

代码比较简单,点击开始录音button后,启动录音,在OlamiVoiceRecognizerListener中回调处理,然后通过handler发送消息用于更新界面。

来看一下初始化view的代码,看看跟java方式书写有哪些不同

private fun initView()

{

mBtnStart = findViewById(R.id.btn_start) asButton

mBtnStop = findViewById(R.id.btn_stop) as Button

mBtnCancel = findViewById(R.id.btn_cancel) asButton

mBtnSend = findViewById(R.id.btn_send) as Button

mInputTextView = findViewById(R.id.tv_inputText) asTextView

mEditText = findViewById(R.id.et_content) asEditText

mTextView = findViewById(R.id.tv_result) asTextView

mTextViewVolume = findViewById(R.id.tv_volume) asTextView

mBtnStart!!.setOnClickListener{

if (mOlamiVoiceRecognizer != null)

mOlamiVoiceRecognizer!!.start()

}

mBtnStop!!.setOnClickListener{

if (mOlamiVoiceRecognizer != null)

mOlamiVoiceRecognizer!!.stop()

mBtnStart!!.text="开始"

Log.i("led","MusicActivity mBtnStop onclick开始")

}

mBtnCancel!!.setOnClickListener{

if (mOlamiVoiceRecognizer != null)

mOlamiVoiceRecognizer!!.cancel()

}

mBtnSend!!.setOnClickListener{

if (mOlamiVoiceRecognizer != null)

mOlamiVoiceRecognizer!!.sendText(mEditText!!.text.toString())

mInputTextView!!.text="输入: "+ mEditText!!.text

}

}

是不是感觉代码更简练了?

下面两句赋值,效果相同,第二句可以用id之间进行文本赋值,比以前简练好多。

mInputTextView!!.text="输入: "+ mEditText!!.text

tv_inputText.text="输入: "+ et_content.text

再来看看handler:

private funinitHandler() {

mHandler = object : Handler() {

override fun handleMessage(msg:Message) {

when (msg.what) {

MessageConst.CLIENT_ACTION_START_RECORED

-> mBtnStart!!.text

="录音中"

MessageConst.CLIENT_ACTION_STOP_RECORED -> mBtnStart!!.text

="识别中"

MessageConst.CLIENT_ACTION_CANCEL_RECORED-> {

mBtnStart!!.text="开始"

mTextView!!.text="已取消"

}

MessageConst.CLIENT_ACTION_ON_ERROR-> {

mTextView!!.text="错误代码:"+ msg.arg1

mBtnStart!!.text="开始"

}

MessageConst.CLIENT_ACTION_UPDATA_VOLUME

-> mTextViewVolume!!.text

="音量: "+ msg.arg1

MessageConst.SERVER_ACTION_RETURN_RESULT-> {

if (msg.obj!= null)

mTextView!!.text="服务器返回: "+ msg.obj.toString()

mBtnStart!!.text="开始"

try {

val message = msg.objas String

var input: String?= null

val jsonObject =JSONObject(message)

val jArrayNli =

jsonObject.optJSONObject("data").optJSONArray("nli")

val jObj =jArrayNli.optJSONObject(0)

var jArraySemantic:JSONArray? = null

if (message.contains("semantic")) {

jArraySemantic= jObj.getJSONArray("semantic")

input =

jArraySemantic!!.optJSONObject(0).optString("input")

} else {

input =jsonObject.optJSONObject("data")

.optJSONObject("asr").optString("result")

}

if (input != null)

mInputTextView!!.text="输入: "+ input

} catch (e: Exception) {

e.printStackTrace()

}

}

}

}

}

}

原来的switch case的方式,变成了when***,代码不仅简练,更贴近现代语言,更容易理解。

上面的MessageConst.SERVER_ACTION_RETURN_RESULT时,获取了服务器返回的结果,紧接着对这段语义进行了简单的解析

{

      "data": {

                  "asr": {

                                "result":"我要听三国演义",

                                "speech_status":0,

                                 "final":true,

                                  "status":0

                      },

                   "nli": [

                             {

                                   "desc_obj": {

                                                           "result":"正在努力搜索中,请稍等",

                                                           "status":0

                                                     },

                                  "semantic": [

                                               {

                                                     "app":"musiccontrol",

                                                     "input":"我要听三国演义",

                                                       "slots": [

                                                                        {

                                                                           "name":"songname",

                                                                             "value":"三国演义"

                                                                          }

                                                                   ],

                                      "modifier": [

                                                            "play"

                                                       ],

              "customer":"58df512384ae11f0bb7b487e"

              }

        ],

         "type":"musiccontrol"

       }

   ]

},

"status":"ok"

}

1)解析出nli中type类型是musiccontrol,这是语法返回app的类型,而这个在线听书的demo只关心musiccontrol这个app类型,其他的忽略。

2)用户说的话转成文字是在asr中的result中获取

3)在nli中的semantic中,input值是用户说的话,同asr中的result。

modifier代表返回的行为动作,此处可以看到是play就是要求播放,slots中的数据表示歌曲名称是三国演义。

那么动作是play,内容是歌曲名称是三国演义,在这个demo中调用

mBookUtil.searchBookAndPlay(songName,0,0);会先查询,查询到结果会再发播放消息要求播放,我要听三国演义这个流程就走完了。

这段是在线听书应用中的语义解析,详情请看博客:http://blog.csdn.net/ls0609/article/details/71519203

6.代码下载

https://github.com/ls0609/WebVoiceRecognize

7.相关博客

语音在线听书博客:http://blog.csdn.net/ls0609/article/details/71519203

语音记账demo:http://blog.csdn.net/ls0609/article/details/72765789

基于JavaScript用olamisdk实现web端语音识别语义理解(speex压缩)

http://blog.csdn.net/ls0609/article/details/73920229

olami开放平台语法编写简介:http://blog.csdn.net/ls0609/article/details/71624340

olami开放平台语法官方介绍:https://cn.olami.ai/wiki/?mp=nli&content=nli2.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容