chivox_aiengine.dart
10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
import 'package:flutter/services.dart';
import 'chivox_aiengine_platform_interface.dart';
class ChivoxAiengineResult {
/// 本次评测唯一标识
String tokenId = "";
/// 是否是本次评测的最后一个结果
bool isLast = false;
/// 文本结果。当结果类型是EvalResult、Error、Vad、SoundIntensity、Other时,使用本字段获取文本结果。
String? text;
/// 二进制结果。当结果类型是BinaryResult时,使用本字段获取二进制数据。
Uint8List? data;
/// 如果录音文件保存成功,返回录音文件的路径,否则返回null。
String? recFilePath;
}
class ChivoxAiengineResultListener {
ChivoxAiengineResultListener(
{void Function(ChivoxAiengineResult result)? onEvalResult,
void Function(ChivoxAiengineResult result)? onBinaryResult,
void Function(ChivoxAiengineResult result)? onError,
void Function(ChivoxAiengineResult result)? onVad,
void Function(ChivoxAiengineResult result)? onSoundIntensity,
void Function(ChivoxAiengineResult result)? onOther}) {
if (onEvalResult != null) this.onEvalResult = onEvalResult;
if (onBinaryResult != null) this.onBinaryResult = onBinaryResult;
if (onError != null) this.onError = onError;
if (onVad != null) this.onVad = onVad;
if (onSoundIntensity != null) this.onSoundIntensity = onSoundIntensity;
if (onOther != null) this.onOther = onOther;
}
/// 用于接收评测分数结果
void Function(ChivoxAiengineResult result) onEvalResult = (result) {};
/// 用于接收二进制结果
void Function(ChivoxAiengineResult result) onBinaryResult = (result) {};
/// 用于接收错误
void Function(ChivoxAiengineResult result) onError = (result) {};
/// 用于接收语音活动检测结果
void Function(ChivoxAiengineResult result) onVad = (result) {};
/// 用于结果音强结果
void Function(ChivoxAiengineResult result) onSoundIntensity = (result) {};
/// 其他未处理的结果,在SDK正确实现的情况下,不会产生此结果
void Function(ChivoxAiengineResult result) onOther = (result) {};
}
class ChivoxAiengine {
static int _callbackCnt = 0;
static String _nextCallbackId() {
_callbackCnt++;
final cnt = _callbackCnt;
final unixTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
return "${unixTime}_$cnt";
}
static bool _staticInited = false;
static const _callbackChannel = MethodChannel("com.chivox.aiengine/callback");
static final Map<String, void Function(List<dynamic>)> _callbacks = {};
static void staticInit() {
if (_staticInited) return;
_staticInited = true;
_callbackChannel.setMethodCallHandler((MethodCall call) async {
if (call.method == "aiengine_callback") {
String callbackId = call.arguments[0];
_callbacks[callbackId]?.call(call.arguments);
}
});
}
bool _loadingOrLoaded = false;
String? _engineId;
ChivoxAiengine._() {
staticInit();
}
/// 无用。忽略本接口
static Future<String?> getPlatformVersion() async {
return await ChivoxAienginePlatform.instance.getPlatformVersion();
}
/// 创建引擎
/// 参数cfg:引擎配置,json字符串(同ios或android端sdk)
/// 抛出异常:PlatformException
static Future<ChivoxAiengine> create(String cfg) async {
final engine = ChivoxAiengine._();
await engine._load(cfg);
return engine;
}
Future<void> _load(String cfg) async {
if (_loadingOrLoaded) {
throw PlatformException(
code: "-1", message: "don't repeatedly load the engine");
}
_loadingOrLoaded = true;
_engineId = await ChivoxAienginePlatform.instance.aiengineNew(cfg);
}
/// 销毁引擎
/// 当应用程序不再需要使用评测引擎时,须销毁,否则引擎内部资源不会得到释放
/// 抛出异常:PlatformException
Future<void> destroy() async {
final engineId = _engineId;
if (engineId == null) return;
_engineId = null;
await ChivoxAienginePlatform.instance.aiengineDelete(engineId);
}
/// 启动一次评测
/// 参数audioSrc: 选择录音模式。其格式如下:
/// {
/// "srcType": string, // [必填项] 音频源:可填"innerRecorder"-sdk内部录音, "outerFeed"-用于外部输入音频
/// "innerRecorderParam": { // [可选项] 内部录音参数
/// "duration": int, // [可选项] 录音时长,默认永久
/// "channel": int, // [可选项] 通道数,默认1
/// "sampleBytes": int, // [可选项] 采样字节,默认2
/// "sampleRate": int, // [可选项] 采样率,默认16000
/// "saveFile": string, // [可选项] 保存录音文件的路径,默认不保存
/// }
/// }
/// 参数param: 语音评测参数,json字符串(同ios或android端sdk)
/// 参数listener: 用于接收评测结果的listener对象
/// 抛出异常:PlatformException
///
/// 注意:对于ios平台,sdk内部并没有设置AudioSessionCategory,应用层须自行设置其为一个支持录音的值。
Future<void> start(Map<String, dynamic> audioSrc, String param,
ChivoxAiengineResultListener listener) async {
final engineId = _engineId;
if (engineId == null) {
throw PlatformException(
code: "-1", message: "the engine has not been loaded yet");
}
final callbackId = _nextCallbackId();
_callbacks[callbackId] = (List<dynamic> args) {
var result = ChivoxAiengineResult();
String type = args[1];
result.isLast = args[2];
result.tokenId = args[3];
result.text = args[4];
result.data = args[5];
result.recFilePath = args[6];
if (type == "evalResult") {
listener.onEvalResult(result);
} else if (type == "binResult") {
listener.onBinaryResult(result);
} else if (type == "error") {
listener.onError(result);
} else if (type == "vad") {
listener.onVad(result);
} else if (type == "soundIntensity") {
listener.onSoundIntensity(result);
} else {
listener.onOther(result);
}
if (result.isLast || type == "error") {
_callbacks.remove(callbackId);
}
// TODO 超过一定时间没有回调,则强行移除回调
};
await ChivoxAienginePlatform.instance
.aiengineStart(engineId, audioSrc, param, callbackId);
return;
}
/// 输入音频数据 (仅outerFeed模式可使用)
/// 抛出异常:PlatformException
Future<void> feed(Uint8List bytes, int length) async {
final engineId = _engineId;
if (engineId == null) {
throw PlatformException(
code: "-1", message: "the engine has not been loaded yet");
}
await ChivoxAienginePlatform.instance.aiengineFeed(engineId, bytes, length);
}
/// 结束录音 (或结束音频数据)
/// 本方法调用后将会进入等待评测结果状态,一旦评测结果产生后,将通过start方法的listener参数回调给应用层。
/// 抛出异常:PlatformException
Future<void> stop() async {
final engineId = _engineId;
if (engineId == null) {
throw PlatformException(
code: "-1", message: "the engine has not been loaded yet");
}
await ChivoxAienginePlatform.instance.aiengineStop(engineId);
}
/// 取消全部评测
/// 本方法调用后,sdk内部取消所有正在进行的评测
/// 抛出异常:PlatformException
Future<void> cancel() async {
final engineId = _engineId;
if (engineId == null) {
throw PlatformException(
code: "-1", message: "the engine has not been loaded yet");
}
await ChivoxAienginePlatform.instance.aiengineCancel(engineId);
}
static Future<String?> getDeviceId() async {
return await ChivoxAienginePlatform.instance.getDeviceId();
}
/// 激活设备,并返回序列号
/// 本方法调用后,sdk会把序列号存储在文件系统中。
/// 应用层下次调用本方法sdk会直接从文件系统中取出该值返回给应用层。
/// 应用层可调用clearSavedSerialNumber方法清除sdk存储的序列号。
/// 参数input: 结构同ios或android端sdk
/// 返回: 结构同ios或andorid端sdk
/// 抛出异常:PlatformException
static Future<String?> getSerialNumber(
ChivoxAiengine? engine, Map input) async {
return await ChivoxAienginePlatform.instance
.getSerialNumber(engine?._engineId, input);
}
/// 清除sdk在文件系统中存储的序列号
/// 抛出异常:PlatformException
static Future<bool?> clearSavedSerialNumber(String appKey) async {
return await ChivoxAienginePlatform.instance.clearSavedSerialNumber(appKey);
}
/// 某些appkey支持二次授权功能,本方法提供该功能
/// 抛出异常:PlatformException
static Future<String?> getProvision(ChivoxAiengine? engine, Map input) async {
return await ChivoxAienginePlatform.instance
.getProvision(engine?._engineId, input);
}
/// 工具方法:提取sdk离线资源包到一个目录中
/// 参数assetPrefix: 离线资源包在flutter assets中的前缀
/// 参数assetNames: 需要提取的资源包的名称列表
/// 参数targetDir: 目标目录,资源包会解压于此
/// 参数progressHint: 解压进度回调
/// 抛出异常:PlatformException
static Future<void> extractRes(String assetPrefix, List<String> assetNames,
String targetDir, Function(double) progressHint) async {
staticInit();
final callbackId = _nextCallbackId();
_callbacks[callbackId] = (List<dynamic> args) {
progressHint(args[1]);
};
await ChivoxAienginePlatform.instance
.extractRes(assetPrefix, assetNames, targetDir, callbackId);
_callbacks.remove(callbackId);
}
/// 工具方法:根据传入的资源根目录和资源名称列表,加载离线资源配置
/// 参数resRootDir: 离线资源包的根目录
/// 参数resNames: 指定需要哪些离线资源包
/// 返回: 一个json字符串,描述了离线资源配置信息。该json用于引擎加载时提供离线配置信息。
/// 抛出异常:PlatformException
static Future<String?> loadNativeCfg(
String resRootDir, List<String> resNames) {
return ChivoxAienginePlatform.instance.loadNativeCfg(resRootDir, resNames);
}
}