# 快速入门
AIUI为开发者提供了多种集成方式,帮助开发者开发出多样化的语音交互应用。
下面以天气查询为例,教您如何快速打造一个天气查询的app,快速了解集成与交互流程。
# Demo体验
我们先体验一下天气查询Demo:
① 用浏览器扫描下方二维码,安装WeatherInquiryDemo(暂时只支持Android手机);
② 打开WeatherInquiryDemo,允许app开启录音权限,点击开始录音就可以开始与它交互了。比如您可以问:合肥的天气怎么样
,北京明天的天气
,查询结果如下图所示:
# 开发步骤
在体验了Demo之后,本节将一步步引导开发者如何完成这个简单的天气查询Demo,开发者可下载完整工程代码 (opens new window)。
# 云端配置
# 创建应用
进入AIUI开放平台 (opens new window),登录您的账号,选择我的应用->创建新应用,填写您应用的相关信息与应用平台后,便创建好应用了。
# 配置技能
应用创建完成后,您可以点击该应用,进入应用配置页面,点击添加技能,勾选天气技能,如需同时使用其他技能,也可以一起勾选。
# SDK集成
本章节仅用Android平台和iOS平台来说明AIUI SDK的集成过程,其他平台接口有略微差别,但过程一致,也可参考此章节进行开发集成。
# 下载SDK
在AIUI开放平台 (opens new window)创建完应用后,在该应用的SDK下载页面即可下载对应的SDK包。SDK包里包含MSC与AIUI库,本章节仅介绍AIUI库的入门使用,MSC库中包含唤醒与合成能力,如需了解MSC库,请访问MSC开发指南 (opens new window)。
# Android 平台集成步骤
# 导入SDK
打开Android Studio,创建一个新的工程,将下载的Android SDK压缩包中libs目录下的libaiui.so以及AIUI.jar拷贝至Android工程的libs目录下,并将SDK包中assets目录下cfg文件夹以及res目录下vad文件夹拷贝至工程中。工程结构如下图所示:
:-:
将AIUI.jar添加至工程依赖,将app module下的gradle配置文件中指定默认jniLibs目录为libs。
android {
...
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
...
}
# 添加用户权限
在工程AndroidManifest.xml文件中添加如下权限,如在Android6.0及以上手机中集成使用,请动态申请所需权限。
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
注意:如需在打包或者生成APK的时候进行混淆,请在proguard.cfg中添加如下代码:
-keep class com.iflytek.**{*;}
-keepattributes Signature
# 创建布局文件
可参考工程源码 (opens new window)添加布局。
# 创建AIUIAgent
SDK中提供的AIUIAgent就是和AIUI交互的桥梁。创建AIUIAgent
,示例如下:
//创建AIUIAgent
AIUIAgent mAIUIAgent = AIUIAgent.createAgent(context,getAIUIParams(),mAIUIListener);
createAgent方法包含三个参数:
- 第一个参数类型为Context;
- 第二个参数类型为String,具体值是通过读取assets目录下的cfg/aiui_phone.cfg文件而获得的字符串;
- 第三个参数类型为AIUIListener,是AIUI事件回调监听器。
getAIUIParams()具体示例如下所示:
private String getAIUIParams() {
String params = "";
AssetManager assetManager = getResources().getAssets();
try {
InputStream ins = assetManager.open( "cfg/aiui_phone.cfg" );
byte[] buffer = new byte[ins.available()];
ins.read(buffer);
ins.close();
params = new String(buffer);
} catch (IOException e) {
e.printStackTrace();
}
return params;
}
mAIUIListener具体示例如下所示:
AIUIListener mAIUIListener = new AIUIListener() {
@Override
public void onEvent(AIUIEvent event) {
switch (event.eventType) {
//唤醒事件
case AIUIConstant.EVENT_WAKEUP:
{
break;
}
//结果事件(包含听写,语义,离线语法结果)
case AIUIConstant.EVENT_RESULT:
{
break;
}
//休眠事件
case AIUIConstant.EVENT_SLEEP:
{
break;
}
// 状态事件
case AIUIConstant.EVENT_STATE: {
mAIUIState = event.arg1;
if (AIUIConstant.STATE_IDLE == mAIUIState) {
// 闲置状态,AIUI未开启
} else if (AIUIConstant.STATE_READY == mAIUIState) {
// AIUI已就绪,等待唤醒
} else if (AIUIConstant.STATE_WORKING == mAIUIState) {
// AIUI工作中,可进行交互
}
} break;
//错误事件
case AIUIConstant.EVENT_ERROR:
{
break;
}
}
}
}
# 语音语义理解示例
发送CMD_WAKEUP消息至AIUI,使AIUI处于唤醒状态,再发送开始录音消息,使麦克风录入音频,并通过AIUIListener的回调,获取语义结果。代码示例如下:
// 先发送唤醒消息,改变AIUI内部状态,只有唤醒状态才能接收语音输入
if( AIUIConstant.STATE_WORKING != mAIUIState ){
AIUIMessage wakeupMsg = new AIUIMessage(AIUIConstant.CMD_WAKEUP, 0, 0, "", null);
mAIUIAgent.sendMessage(wakeupMsg);
}
// 打开AIUI内部录音机,开始录音
String params = "sample_rate=16000,data_type=audio";
AIUIMessage writeMsg = new AIUIMessage( AIUIConstant.CMD_START_RECORD, 0, 0, params, null );
mAIUIAgent.sendMessage(writeMsg);
如出现20006错误,请注意下应用是否拥有录音权限。返回的语义结果,参考语义结果说明文档
# 结果解析
在AIUIEventListener回调中,可以收到来自AIUI的多种消息,具体示例如下:
private AIUIListener mAIUIListener = new AIUIListener() {
@Override
public void onEvent(AIUIEvent event) {
switch (event.eventType) {
case AIUIConstant.EVENT_WAKEUP:
//唤醒事件
Log.i( TAG, "on event: "+ event.eventType );
break;
case AIUIConstant.EVENT_RESULT: {
//结果解析事件
try {
JSONObject bizParamJson = new JSONObject(event.info);
JSONObject data = bizParamJson.getJSONArray("data").getJSONObject(0);
JSONObject params = data.getJSONObject("params");
JSONObject content = data.getJSONArray("content").getJSONObject(0);
if (content.has("cnt_id")) {
String cnt_id = content.getString("cnt_id");
JSONObject cntJson = new JSONObject(new String(event.data.getByteArray(cnt_id), "utf-8"));
String sub = params.optString("sub");
if ("nlp".equals(sub)) {
// 解析得到语义结果
String resultStr = cntJson.optString("intent");
Log.i( TAG, resultStr );
}
}
} catch (Throwable e) {
e.printStackTrace();
}
} break;
case AIUIConstant.EVENT_ERROR: {
//错误事件
Log.i( TAG, "on event: "+ event.eventType );
Log.e(TAG, "错误: "+event.arg1+"\n"+event.info );
} break;
case AIUIConstant.EVENT_VAD: {
if (AIUIConstant.VAD_BOS == event.arg1) {
//语音前端点
} else if (AIUIConstant.VAD_EOS == event.arg1) {
//语音后端点
}
} break;
case AIUIConstant.EVENT_START_RECORD: {
Log.i( TAG, "on event: "+ event.eventType );
//开始录音
} break;
case AIUIConstant.EVENT_STOP_RECORD: {
Log.i( TAG, "on event: "+ event.eventType );
// 停止录音
} break;
case AIUIConstant.EVENT_STATE: {
// 状态事件
mAIUIState = event.arg1;
if (AIUIConstant.STATE_IDLE == mAIUIState) {
// 闲置状态,AIUI未开启
} else if (AIUIConstant.STATE_READY == mAIUIState) {
// AIUI已就绪,等待唤醒
} else if (AIUIConstant.STATE_WORKING == mAIUIState) {
// AIUI工作中,可进行交互
}
} break;
default:
break;
}
}
};
# iOS 平台集成步骤
# 导入SDK
开发者需要将下载的iOS SDK压缩包中l的iflyAIUI.framework添加到开发者工程中,配置iflyAIUI.framework路径。假设开发者的Xcode工程目录与iflyAIUI.framework的目录位置关系是如下图所示这样的:
那么,开发者需要注意正确设置iflyAIUI.framework的路径: 依次点击TARGETS -> Build Setting -> Framework Search Path,双击修改路径,如下图所示。
# 添加配置文件和资源文件
开发者需要将AIUIDemo中resource文件夹添加到开发者自己的工程中,该文件夹下包括AIUI需要读取的配置参数文件和语音识别过程中需要的vad资源。
# 添加系统库依赖
开发者集成AIUI SDK可以复用AIUIDemo中的部分源码以提高开发效率。AIUIService.h、AIUIService.mm、TTSViewController.h、TTSViewController.m、UnderstandViewController.h以及UnderstandViewController.mm 等文件实现了AIUI SDK 接口调用参考示例,由于这些文件使用C++和OC编写,开发者在使用XCode进行编译时要额外注意添加libicucore.tbd、libc++.tbd和libz.tbd三个系统库依赖。如下图所示:
# 设置Bitcode
Xcode 7,8默认开启了Bitcode,而Bitcode 需要工程依赖的所有类库同时支持。AIUI SDK暂时还不支持Bitcode,需要开发者关闭该设置。只需在Targets -> Build Settings 中搜索Bitcode 即可,找到相应选项,设置为NO。 如下图所示:
# 用户隐私权限配置
iOS 10发布以来,苹果为了用户信息安全,加入隐私权限设置机制,让用户来选择是否允许。 隐私权限配置可在info.plist 新增相关privacy字段,AIUI SDK中需要用到的权限主要包括麦克风权限、联系人权限和地理位置权限:
<key>NSMicrophoneUsageDescription</key>
<string></string>
<key>NSLocationUsageDescription</key>
<string></string>
<key>NSLocationAlwaysUsageDescription</key>
<string></string>
<key>NSContactsUsageDescription</key>
<string></string>
即在Info.plist 中增加下图设置:
# 初始化工作目录
AppDelegate中初始化,设置日志是否开启、日志打印级别和设置工作目录等。初始化设置不强制要求放在在AppDelegate中,只需要保证在创建AIUI客户端代理之前调用即可,开发者可以自行选择。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [paths objectAtIndex:0];
cachePath = [cachePath stringByAppendingString:@"/"];
NSLog(@"cachePath=%@",cachePath);
[IFlyAIUISetting setSaveDataLog:NO];
[IFlyAIUISetting setLogLevel:LV_INFO];
[IFlyAIUISetting setAIUIDir:cachePath];
[IFlyAIUISetting setMscDir:cachePath];
return YES;
}
# 创建AIUIAgent
SDK中提供的AIUIAgent就是和AIUI交互的桥梁。先创建AIUIAgent,然后发送唤醒消息使AIUI处于Working状态,示例如下:
@property IFlyAIUIAgent *aiuiAgent;
// 读取aiui.cfg配置文件
NSString *cfgFilePath = [[NSBundle mainBundle] pathForResource:@"aiui" ofType:@"cfg"];
NSString *cfg = [NSString stringWithContentsOfFile:cfgFilePath encoding:NSUTF8StringEncoding error:nil];
//创建AIUIAgent
_aiuiAgent = [IFlyAIUIAgent createAgent:cfg withListener:self];
# 语音语义理解示例
发送CMD_WAKEUP消息至AIUI,使AIUI处于唤醒状态,再发送开始录音消息,使麦克风录入音频。代码示例如下:
//发送唤醒消息
IFlyAIUIMessage *wakeuMsg = [[IFlyAIUIMessage alloc]init];
wakeuMsg.msgType = CMD_WAKEUP;
[_aiuiAgent sendMessage:wakeuMsg];
//发送开始录音消息
IFlyAIUIMessage *msg = [[IFlyAIUIMessage alloc] init];
msg.msgType = CMD_START_RECORD;
[_aiuiAgent sendMessage:msg];
# 结果回调
实现IFlyAIUIListener协议类接口,在onEvent事件获取结果回调。代码示例如下:
- (void) onEvent:(IFlyAIUIEvent *) event {
switch (event.eventType) {
case EVENT_CONNECTED_TO_SERVER:
{
//服务器连接成功事件
NSLog(@"CONNECT TO SERVER");
} break;
case EVENT_SERVER_DISCONNECTED:
{
//服务器连接断开事件
NSLog(@"DISCONNECT TO SERVER");
} break;
case EVENT_START_RECORD:
{
//开始录音事件
NSLog(@"EVENT_START_RECORD");
} break;
case EVENT_STOP_RECORD:
{
//停止录音事件
NSLog(@"EVENT_STOP_RECORD");
} break;
case EVENT_STATE:
{
//AIUI运行状态事件
switch (event.arg1)
{
case STATE_IDLE:
{
NSLog(@"EVENT_STATE: %s", "IDLE");
} break;
case STATE_READY:
{
NSLog(@"EVENT_STATE: %s", "READY");
} break;
case STATE_WORKING:
{
NSLog(@"EVENT_STATE: %s", "WORKING");
} break;
}
} break;
case EVENT_WAKEUP:
{
//唤醒事件
NSLog(@"EVENT_WAKEUP");
} break;
case EVENT_SLEEP:
{
//休眠事件
NSLog(@"EVENT_SLEEP");
} break;
case EVENT_VAD:
{
switch (event.arg1)
{
case VAD_BOS:
{
//前端点事件
NSLog(@"EVENT_VAD_BOS");
} break;
case VAD_EOS:
{
//后端点事件
NSLog(@"EVENT_VAD_EOS");
} break;
case VAD_VOL:
{
//音量事件
NSLog(@"vol: %d", event.arg2);
} break;
}
} break;
case EVENT_RESULT:
{
NSLog(@"EVENT_RESULT");
[self processResult:event];
} break;
case EVENT_CMD_RETURN:
{
NSLog(@"EVENT_CMD_RETURN");
} break;
case EVENT_ERROR:
{
NSString *error = [[NSString alloc] initWithFormat:@"Error Message:%@\nError Code:%d",event.info,event.arg1];
NSLog(@"EVENT_ERROR: %@",error);
} break;
}
}
//处理结果
- (void)processResult:(IFlyAIUIEvent *)event{
NSString *info = event.info;
NSData *infoData = [info dataUsingEncoding:NSUTF8StringEncoding];
NSError *err;
NSDictionary *infoDic = [NSJSONSerialization JSONObjectWithData:infoData options:NSJSONReadingMutableContainers error:&err];
if(!infoDic){
NSLog(@"parse error! %@", info);
return;
}
NSLog(@"infoDic = %@", infoDic);
NSDictionary *data = [((NSArray *)[infoDic objectForKey:@"data"]) objectAtIndex:0];
NSDictionary *params = [data objectForKey:@"params"];
NSDictionary *content = [(NSArray *)[data objectForKey:@"content"] objectAtIndex:0];
NSString *sub = [params objectForKey:@"sub"];
if([sub isEqualToString:@"nlp"]){
NSString *cnt_id = [content objectForKey:@"cnt_id"];
if(!cnt_id){
NSLog(@"Content Id is empty");
return;
}
NSData *rltData = [event.data objectForKey:cnt_id];
if(rltData){
NSString *rltStr = [[NSString alloc]initWithData:rltData encoding:NSUTF8StringEncoding];
NSLog(@"nlp result: %@", rltStr);
}
} else if([sub isEqualToString:@"tts"]){
NSLog(@"receive tts event");
NSString *cnt_id = [content objectForKey:@"cnt_id"];
if(cnt_id){
//合成音频数据
NSData *audioData = [event.data objectForKey:cnt_id];
//当前音频块状态:0(开始),1(中间),2(结束),3(一块)
int dts = [(NSNumber *)[content objectForKey:@"dts"] intValue];
//合成进度
int text_per = [(NSNumber *)[content objectForKey:@"text_percent"] intValue];
NSLog(@"dataLen=%lu, dts=%d, text_percent=%d", (unsigned long)[audioData length], dts, text_per);
}
}
}
# 交互体验
将构建好的app安装到手机中,开发者可以用日常交流方式与刚写好的Demo进行自然交互,如今天天气怎么样
、合肥的天气
等,AIUI会将语义结果返回并显示在app中。更多的技能支持与说法请参考AIUI技能商店。