本文介绍如何在你的项目中集成和使用讯飞语音实时转写&翻译(多语种)插件(以下简称“讯飞转写&翻译插件”),包括 Android、iOS 和 Windows 平台。
你还可以在 Web 应用中使用讯飞转写&翻译插件,详见 Web 使用说明。
讯飞转写&翻译插件的工作流程如下:
下载示例项目,查看完整的项目结构和示例代码:
平台 | 语言 | 示例项目 |
---|---|---|
Android | Java | 点击下载 |
iOS | Objective-C | 点击下载 |
Windows | C++ | 点击下载 |
Android
AgoraWithHyAndroid/app/src/main/java/agoramarketplace/hy/demo/MainActivity.java
文件,将 #YOUR APP ID#
替换为你的 App ID。获取 App ID 请参考开始使用声网平台。private static final String appId = "#YOUR APP ID#";
AgoraWithHyAndroid/app/src/main/java/agoramarketplace/hy/demo/HyUtil.java
文件,填入购买和激活插件时获取的 appid
、appKey
和 appSecret
。/** 应用标识,对应在声网控制台购买插件后、由讯飞提供的 appid。 */
public static final String APP_ID = "";
/** API密钥,对应在声网控制台购买插件后、由讯飞提供的 appKey。 */
public static final String API_KEY = "";
/** API秘密,对应在声网控制台购买插件后、由讯飞提供的 appSecret */
public static final String API_SECRET = "";
iOS
AgoraWithHyIos/AgoraWithHyIos.xcodeproj
。AppID.m
文件,将 #YOUR APP ID#
替换为你的 App ID。获取 App ID 请参考开始使用声网平台。NSString *const appID = @"#YOUR APP ID#";
AgoraWithHyIos/VideoChatViewController.m
文件,填入购买和激活插件时获取的 appid
、appKey
和 appSecret
。//"app_id"、"api_key"、"api_secret" 分别对应在声网控制台购买插件后、由讯飞提供的 appid、appKey 和 appSecret
NSDictionary *commonDict = @{@"app_id":@"",@"api_key":@"",@"api_secret":@""};
Windows
AgoraWithHyWin/AgoraWithHyWin.sln
,并且安装 MFC 组件。AgoraWithHyWinDlg.h
文件,将 #YOUR APP ID#
替换为你的 App ID。获取 App ID 请参考开始使用声网平台。#define AGORA_APP_ID "#YOUR APP ID#"
HyUtil.h
文件,填入购买和激活插件时获取的 appid
、appKey
和 appSecret
。/** 应用标识,对应在声网控制台购买插件后、由讯飞提供的 appid。 */
#define EXTENSION_APP_ID ""
/** API密钥,对应在声网控制台购买插件后、由讯飞提供的 appKey。 */
#define EXTENSION_API_KEY ""
/** API秘密,对应在声网控制台购买插件后、由讯飞提供的 appSecret */
#define EXTENSION_API_SECRET ""
运行成功后,示例项目会安装到你的 Android、iOS 或 Windows 设备上。
中译英
下拉控件, start
、 flush
、 stop
按钮, tip
、 ist_text
、 its_text
文本视图。中译英
下拉控件选择参数。start
按钮启动倾听。ist_text
文本视图中,文本翻译文本会显示在 its_text
文本视图中。flush
按钮结束音频获取结果,或单击 stop
按钮停止倾听。tip
文本视图会显示 onEnd
。讯飞转写&翻译插件插件需要与声网音频 SDK v4.x 搭配使用。参考以下文档集成音频 SDK v4.x 并实现基础的语音通话:
在声网控制台购买和激活讯飞转写&翻译插件。购买成功后,你会收到由讯飞提供的 appid
、appKey
和 appSecret
,后续启动倾听时需要用到。
参考如下步骤在你的项目中集成讯飞转写&翻译插件:
Android
.aar
文件保存到项目文件夹的 /app/libs
路径。app/build.gradle
文件,在 dependencies
中添加如下行:implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"])
import io.agora.hy.extension.ExtensionManager;
iOS
.framework
文件保存到你的项目文件夹下。以如下项目结构为例,你可以把插件保存到 frameworks
路径下。
.
├── <ProjectName>
│ ├── frameworks
├── <ProjectName>.xcodeproj
Windows
点击下载讯飞语音实时转写&翻译(多语种) 的 Windows 插件包。
解压文件夹,将所有 .dll
文件保存到项目生成的 .exe
文件所在的目录下。若要保存到其他目录,需要在调用loadExtensionProvider
时传入对应的路径。
本节介绍插件相关接口的调用流程。接口的参数解释详见接口说明。
RtcEngineConfig
实例调用 addExtension
,将插件添加到 SDK 中。
config.addExtension(ExtensionManager.EXTENSION_NAME);
RtcEngineConfig
实例设置 mExtensionObserver
监听器。
private final IMediaExtensionObserver mMediaExtensionObserver = new IMediaExtensionObserver() {
@Override
public void onEvent(String provider, String extension, String key, String value) {
Log.i(TAG, "onEvent | provider: " + provider + ", extension: " + extension
+ ", key: " + key + ", value: " + value);
if (!ExtensionManager.EXTENSION_VENDOR_NAME.equals(provider)
|| !ExtensionManager.EXTENSION_AUDIO_FILTER_NAME.equals(extension)) {
return;
}
mHyUtil.onEvent(key, value);
}
}
config.mExtensionObserver = mMediaExtensionObserver;
在加入频道前,调用 enableExtension
开启插件。
mRtcEngine.enableExtension(ExtensionManager.EXTENSION_VENDOR_NAME, ExtensionManager.EXTENSION_AUDIO_FILTER_NAME, true);
插件设置 logcat 日志等级。
mRtcEngine.setExtensionProviderProperty(ExtensionManager.EXTENSION_VENDOR_NAME, "log_lvl", "" + 8);
空闲时可调用,调用后会进入会话。会话最长5小时。
你需要填入购买和激活插件时获取的 appid
、appKey
和 appSecret
。
public void start(ParamWrap paramWrap) {
mParser.clear();
String val = null;
try {
JSONObject rootJo = new JSONObject();
// 公共对象。必选。
JSONObject commonJo = new JSONObject();
{
// 应用标识。必选。对应在声网控制台购买插件后、由讯飞提供的 appid。
commonJo.put("app_id", APP_ID);
// API 密钥。必选。对应在声网控制台购买插件后、由讯飞提供的 appKey。
commonJo.put("api_key", API_KEY);
// API 秘密。必选。对应在声网控制台购买插件后、由讯飞提供的 appSecret。
commonJo.put("api_secret", API_SECRET);
}
rootJo.put("common", commonJo);
// 语音转写对象。必选。
JSONObject istJo = new JSONObject();
{
// URI。必选。
istJo.put("uri", "wss://ist-api.xfyun.cn/v2/ist");
// 请求对象。必选。
JSONObject reqJo = new JSONObject();
{
// 业务对象。必选。
JSONObject businessJo = new JSONObject();
{
// 语种。必选。
businessJo.put("language", paramWrap.mIstLanguage);
// 口音。必选。
businessJo.put("accent", paramWrap.mIstAccent);
// 领域。必选。
businessJo.put("domain", paramWrap.mIstDomain);
// 语言类型
// 值类型:int
// 值范围:
// 1:中英文模式,中文英文均可识别;
// 3:英文模式,只识别出英文
// 值默认:1
businessJo.put("language_type", paramWrap.mIstLanguageType);
// 动态修正
businessJo.put("dwa", paramWrap.mIstDwa);
}
reqJo.put("business", businessJo);
}
istJo.put("req", reqJo);
}
rootJo.put("ist", istJo);
// 文本翻译对象。
JSONObject itsJo = new JSONObject();
{
// URI。必选。
itsJo.put("uri", "https://itrans.xfyun.cn/v2/its");
// 请求对象。必选。
JSONObject reqJo = new JSONObject();
{
// 业务对象。必选。
JSONObject businessJo = new JSONObject();
{
// 源语种。必选。
businessJo.put("from", paramWrap.mItsFrom);
// 目标语种。必选。
businessJo.put("to", paramWrap.mItsTo);
}
reqJo.put("business", businessJo);
}
itsJo.put("req", reqJo);
}
rootJo.put("its", itsJo);
val = rootJo.toString();
} catch (JSONException e) {
mListener.onLogE(TAG + ".start | json fail", e);
return;
}
int errCode = mRtcEngine.setExtensionProperty(ExtensionManager.EXTENSION_VENDOR_NAME,
ExtensionManager.EXTENSION_AUDIO_FILTER_NAME, "start_listening", val);
mListener.onLogI(TAG + ".start | mRtcEngine.setExtensionProperty errCode: " + errCode);
}
会话时可调用,调用后会先处理完音频和结果,再进入空闲,回调 "end"
。
public void flush() {
// 值不能为空,否则收不到。
int errCode = mRtcEngine.setExtensionProperty(ExtensionManager.EXTENSION_VENDOR_NAME, ExtensionManager.EXTENSION_AUDIO_FILTER_NAME, "flush_listening", "{}");
mListener.onLogI(TAG + ".flush | mRtcEngine.setExtensionProperty errCode: " + errCode);
}
讯飞转写&翻译插件会通过 onEvent
回调返回语音转写和文本翻译结果。识别结果的含义详见 onEvent 回调。
会话时可调用,调用后会先丢弃音频和结果,再进入空闲,回调 "end"
。
/**
* 停止倾听
*/
public void stop() {
int errCode = mRtcEngine.setExtensionProperty(ExtensionManager.EXTENSION_VENDOR_NAME, ExtensionManager.EXTENSION_AUDIO_FILTER_NAME, "stop_listening", "{}");
mListener.onLogI(TAG + ".stop | mRtcEngine.setExtensionProperty errCode: " + errCode);
}
本节介绍插件相关接口的调用流程。接口的参数解释详见接口说明。
开启插件
AgoraRtcEngineConfig *cfg = [AgoraRtcEngineConfig new];
cfg.appId = appID;
cfg.eventDelegate = self;
self.agoraKit = [AgoraRtcEngineKit sharedEngineWithConfig:cfg delegate:self];
[self.agoraKit enableExtensionWithVendor:@"Hy" extension:@"IstIts" enabled:YES];
实现代理方法
#pragma mark - AgoraMediaFilterEventDelegate (self需要实现代理方法)
- (void)onEvent:(NSString * _Nullable)vendor extension:(NSString *)extension key:(NSString * _Nullable)key value:(NSString * _Nullable)value {}
插件设置 logcat 日志等级。
[self.agoraKit setExtensionProviderPropertyWithVendor:@"Hy" key:@"log_lvl" value:@"4"];
空闲时可调用,调用后会进入会话。会话最长5小时。
你需要填入购买和激活插件时获取的 appid
、appKey
和 appSecret
。
- (void)hyStart:(id)obj{
[itsDataDict removeAllObjects];
[istDataDict removeAllObjects];
NSString *type =@"中译英";
NSMutableDictionary *rootDict = [NSMutableDictionary dictionary];
// "app_id"、"api_key"、"api_secret" 分别对应在声网控制台购买插件后、由讯飞提供的 appid、appKey 和 appSecret
NSDictionary *commonDict = @{@"app_id":@"",@"api_key":@"",@"api_secret":@""};
[rootDict setObject:commonDict forKey:@"common"];
NSMutableDictionary *istDict = [NSMutableDictionary dictionary];
[istDict setObject:@"wss://ist-api.xfyun.cn/v2/ist" forKey:@"uri"];
NSMutableDictionary *itsDict = [NSMutableDictionary dictionary];
[itsDict setObject:@"https://itrans.xfyun.cn/v2/its" forKey:@"uri"];
if([mSelectedType isEqualToString:type]){
NSDictionary *istBusinessDict = @{@"language":@"zh_cn",@"accent":@"mandarin",@"domain":@"ist_ed_open",@"language_type":@1,@"dwa":@"wpgs"};
NSDictionary *istReqDict = @{@"business":istBusinessDict};
[istDict setObject:istReqDict forKey:@"req"];
[rootDict setObject:istDict forKey:@"ist"];
NSDictionary *itsBusinessDict = @{@"from":@"cn",@"to":@"en"};
NSDictionary *itsReqDict = @{@"business":itsBusinessDict};
[itsDict setObject:itsReqDict forKey:@"req"];
[rootDict setObject:itsDict forKey:@"its"];
}else{
NSDictionary *businessDict = @{@"language":@"zh_cn",@"accent":@"mandarin",@"domain":@"ist_ed_open",@"language_type":@3,@"dwa":@"wpgs"};
NSDictionary *reqDict = @{@"business":businessDict};
[istDict setObject:reqDict forKey:@"req"];
[rootDict setObject:istDict forKey:@"ist"];
NSDictionary *itsBusinessDict = @{@"from":@"en",@"to":@"cn"};
NSDictionary *itsReqDict = @{@"business":itsBusinessDict};
[itsDict setObject:itsReqDict forKey:@"req"];
[rootDict setObject:itsDict forKey:@"its"];
}
NSData *data=[NSJSONSerialization dataWithJSONObject:rootDict options:NSJSONWritingPrettyPrinted error:nil];
NSString *str=[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
[self.agoraKit setExtensionPropertyWithVendor:@"Hy" extension:@"IstIts" key:@"start_listening" value:str];
}
会话时可调用,调用后会先处理完音频和结果,再进入空闲,回调 "end"
。
- (void)hyFlush:(id)obj{
[self.agoraKit setExtensionPropertyWithVendor:@"Hy" extension:@"IstIts" key:@"flush_listening" value:@"{}"];
}
讯飞转写&翻译插件会通过 onEvent
回调返回语音转写和文本翻译结果。识别结果的含义详见 onEvent 回调。
会话时可调用,调用后会先丢弃音频和结果,再进入空闲,回调 "end"
。
- (void)hyStop:(id)obj{
[self.agoraKit setExtensionPropertyWithVendor:@"Hy" extension:@"IstIts" key:@"stop_listening" value:@"{}"];
}
本节介绍插件相关接口的调用流程。接口的参数解释详见接口说明。
初始化 IRtcEngine
前,在 RtcEngineContext
实例中设置插件事件回调 IRtcEngineEventHandler
。
class CAgoraWithHyWinEventHandler: public agora::rtc::IRtcEngineEventHandler{
public:
// 插件事件回调。
virtual void onExtensionEvent(const char * provider, const char * extension, const char * ky, const char * value) override {
}
private: HWND m_hMsgHanlder = NULL;
HyUtil * mHyUtilPtr = NULL;
};
m_rtcEngine = createAgoraRtcEngine();
CAgoraWithHyWinEventHandler m_eventHandler;
agora::rtc::RtcEngineContext context;
······
context.eventHandler = &m_eventHandler;
······
ret = m_rtcEngine->initialize(context);
调用 loadExtensionProvider
,将插件添加到 SDK 中。
ret = m_rtcEngine->loadExtensionProvider("libagora-hy.dll");
在加入频道前,调用 enableExtension
开启插件。
ret = m_rtcEngine->enableExtension(EXTENSION_PROVIDER_NAME, EXTENSION_AUDIO_FILTER_NAME, true);
插件设置 logcat 日志等级,需要在enableVideo
和enableAudio
之前调用。
m_rtcEngine->setExtensionProviderProperty(EXTENSION_PROVIDER_NAME, "log_lvl", "8");
空闲时可调用,调用后会进入会话。会话最长5小时。
你需要填入购买和激活插件时获取的 appid
、appKey
和 appSecret
。
int HyUtil::start(ParamWrap param) {
mParser.clear();
Json::Value root;
// 公共对象。必选。
Json::Value common; {
// 应用标识。必选。对应在声网控制台购买插件后、由讯飞提供的 appid。
common["app_id"] = EXTENSION_APP_ID;
// API密钥。必选。对应在声网控制台购买插件后、由讯飞提供的 appKey。
common["api_key"] = EXTENSION_API_KEY;
// API秘密。必选。对应在声网控制台购买插件后、由讯飞提供的 appSecret。
common["api_secret"] = EXTENSION_API_SECRET;
}
root["common"] = common;
// 语音转写对象。必选。
Json::Value ist; {
// URI。必选。
ist["uri"] = EXTENSION_API_IST_URI;
// 请求对象。必选。
Json::Value req; {
Json::Value business; {
// 语种。必选。
business["language"] = param.mIstLanguage;
// 口音。必选。
business["accent"] = param.mIstAccent;
// 领域。必选。
business["domain"] = param.mIstDomain;
// 语言类型
// 值类型:int
// 值范围:
// 1:中英文模式,中文英文均可识别;
// 3:英文模式,只识别出英文
// 值默认:1
business["language_type"] = param.mIstLanguageType;
// 动态修正
business["dwa"] = param.mIstDwa;
}
req["business"] = business;
}
ist["req"] = req;
}
root["ist"] = ist;
// 文本翻译对象。
Json::Value its; {
// URI。必选。
its["uri"] = EXTENSION_API_ITS_URI;
// 请求对象。必选。
Json::Value req; {
// 业务对象。必选。
Json::Value business; {
// 源语种。必选。
business["from"] = param.mItsFrom;
// 目标语种。必选。
business["to"] = param.mItsTo;
}
req["business"] = business;
}
its["req"] = req;
}
root["its"] = its;
string val = root.toStyledString();
int errCode = m_rtcEngine->setExtensionProperty(EXTENSION_PROVIDER_NAME, EXTENSION_AUDIO_FILTER_NAME, "start_listening", val.c_str());
return errCode;
}
会话时可调用,调用后会先处理完音频和结果,再进入空闲,回调 "end"
。
int HyUtil::flush() {
return m_rtcEngine->setExtensionProperty(EXTENSION_PROVIDER_NAME, EXTENSION_AUDIO_FILTER_NAME, "flush_listening", "{}");
}
讯飞转写&翻译插件会通过 onEvent
回调返回语音转写和文本翻译结果。识别结果的含义详见 onEvent 回调。
会话时可调用,调用后会先丢弃音频和结果,再进入空闲,回调 "end"
。
int HyUtil::stop() {
return m_rtcEngine->setExtensionProperty(EXTENSION_PROVIDER_NAME, EXTENSION_AUDIO_FILTER_NAME, "stop_listening", "{}");
}
插件所有相关接口的参数解释详见接口说明。
1. 在 MacBook 上运行 Android 示例项目为什么会编译失败?
由于示例项目中 Android NDK 的路径默认为 Windows 设备上的路径,因此在 MacBook 上会编译失败并提示 Location specified by ndk.dir did not contain a valid NDK
。参考以下步骤解决:
local.properties
文件,将 ndk.dir
的路径替换为你刚才复制的路径,并且保存修改。