Commit 14b179e4 by tanghuan

集成声通口语评测SDK,并增加相关指令

1 parent ca90354c
......@@ -43,6 +43,11 @@ android {
manifestPlaceholders["VIVO_APPID"] = "105993977"
// manifestPlaceholders["HONOR_APPID"] = "您应用分配的证书 APPID"
// 声通
ndk {
abiFilters += listOf("arm64-v8a", "armeabi-v7a")
}
}
// 添加签名配置
......@@ -84,6 +89,13 @@ android {
)
}
}
// 声通SDK资源
sourceSets {
getByName("main") {
jniLibs.srcDirs("libs")
}
}
}
flutter {
......
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type
This file is too large to display.
......@@ -9,6 +9,7 @@ import 'package:appframe/config/routes.dart';
import 'package:appframe/data/models/message/h5_message.dart';
import 'package:appframe/services/dispatcher.dart';
import 'package:appframe/services/im_service.dart';
import 'package:appframe/services/kouyu_service.dart';
import 'package:appframe/services/local_server_service.dart';
import 'package:appframe/services/player_service.dart';
import 'package:appframe/services/recorder_service.dart';
......@@ -202,6 +203,7 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
HttpServer? _server;
PlayerService? _playerService;
RecorderService? _recorderService;
KouyuService? _kouyuService;
WebViewController get controller => _controller;
......@@ -275,6 +277,7 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
_playerService = getIt.get<PlayerService>();
_playerService?.sendResponse = _sendResponse;
_recorderService = getIt.get<RecorderService>();
_kouyuService = getIt.get<KouyuService>();
// 登录IM
_loginIM();
......@@ -1205,6 +1208,7 @@ class WebCubit extends Cubit<WebState> with WidgetsBindingObserver {
await _playerService?.close();
await _recorderService?.close();
await _kouyuService?.dispose();
if (Platform.isIOS) {
WebCubitHolder.unregister();
......
......@@ -98,6 +98,13 @@ class Constant {
///
static const String classIdSetKey = 'auth_class_ids';
/// 声通口语测评
/// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
static const String stAppKey = EnvConfig.env == 'dev' ? '1765336128000622' : '1765336128000622';
static const String stSecretKey =
EnvConfig.env == 'dev' ? '7be64a379ccf8ebef774c45b8dd84ed3' : '7be64a379ccf8ebef774c45b8dd84ed3';
/// 测试阶段使用
static const bool needIM = false;
static const bool needUpgrade = true;
......
......@@ -14,6 +14,7 @@ import 'package:appframe/data/repositories/message/device_info_handler.dart';
import 'package:appframe/data/repositories/message/download_file_handler.dart';
import 'package:appframe/data/repositories/message/go_login_handler.dart';
import 'package:appframe/data/repositories/message/image_info_handler.dart';
import 'package:appframe/data/repositories/message/kouyu_handler.dart';
import 'package:appframe/data/repositories/message/location_handler.dart';
import 'package:appframe/data/repositories/message/network_type_handler.dart';
import 'package:appframe/data/repositories/message/open_document_handler.dart';
......@@ -38,6 +39,7 @@ import 'package:appframe/data/repositories/wechat_auth_repository.dart';
import 'package:appframe/services/api_service.dart';
import 'package:appframe/services/dispatcher.dart';
import 'package:appframe/services/im_service.dart';
import 'package:appframe/services/kouyu_service.dart';
import 'package:appframe/services/local_server_service.dart';
import 'package:appframe/services/player_service.dart';
import 'package:appframe/services/recorder_service.dart';
......@@ -165,6 +167,13 @@ Future<void> setupLocator() async {
getIt.registerLazySingleton<MessageHandler>(() => AudioStopHandler(), instanceName: 'audioStop');
getIt.registerLazySingleton<MessageHandler>(() => AudioClearHandler(), instanceName: 'audioClear');
/// 口语测评
getIt.registerLazySingleton<MessageHandler>(() => KouyuStartHandler(), instanceName: 'kouyuStart');
getIt.registerLazySingleton<MessageHandler>(() => KouyuStopHandler(), instanceName: 'kouyuStop');
getIt.registerLazySingleton<MessageHandler>(() => KouyuCancelHandler(), instanceName: 'kouyuCancel');
getIt.registerLazySingleton<MessageHandler>(() => KouyuPlaybackHandler(), instanceName: 'kouyuPlayback');
getIt.registerLazySingleton<MessageHandler>(() => KouyuStopPlayHandler(), instanceName: 'kouyuStopPlay');
/// 裁剪图片
getIt.registerLazySingleton<MessageHandler>(() => CropImageHandler(), instanceName: 'cropImage');
......@@ -216,6 +225,9 @@ Future<void> setupLocator() async {
/// 录音
getIt.registerSingleton<RecorderService>(RecorderService());
/// 口语
getIt.registerSingleton<KouyuService>(KouyuService());
/// repository
getIt.registerLazySingleton<WechatAuthRepository>(() => WechatAuthRepository());
getIt.registerLazySingleton<PhoneAuthRepository>(() => PhoneAuthRepository());
......
import 'package:appframe/config/locator.dart';
import 'package:appframe/services/dispatcher.dart';
import 'package:appframe/services/kouyu_service.dart';
class KouyuStartHandler extends MessageHandler {
@override
Future<dynamic> handleMessage(dynamic params) async {
if (params is! Map<String, dynamic>) {
throw Exception('参数错误');
}
var lang = params['lang'] as String;
var type = params['type'] as int;
var text = params['text'] as String;
if (lang != 'cn' && lang != 'en') {
throw Exception('参数错误');
}
String coreType;
if(lang=='cn') {
if (type == 1) {
coreType = 'word.eval.cn';
} else if (type == 2) {
coreType = 'sent.eval.cn';
} else if (type == 3) {
coreType = 'para.eval.cn';
} else {
throw Exception('参数错误');
}
} else{
if (type == 1) {
coreType = 'word.eval';
} else if (type == 2) {
coreType = 'sent.eval';
} else if (type == 3) {
coreType = 'para.eval';
} else {
throw Exception('参数错误');
}
}
return await getIt.get<KouyuService>().start(coreType, text);
}
}
class KouyuStopHandler extends MessageHandler {
@override
Future<dynamic> handleMessage(dynamic params) async {
Map<String, dynamic> result = await getIt.get<KouyuService>().stop();
if (result.containsKey('errId')) {
int errId = result['errId'];
if (errId != 0) {
throw Exception('$errId|${result['error']}');
} else {
throw Exception('${result['error']}');
}
}
return result;
}
}
class KouyuCancelHandler extends MessageHandler {
@override
Future<dynamic> handleMessage(dynamic params) async {
return await getIt.get<KouyuService>().cancel();
}
}
class KouyuPlaybackHandler extends MessageHandler {
@override
Future<dynamic> handleMessage(dynamic params) async {
return getIt.get<KouyuService>().playback();
}
}
class KouyuStopPlayHandler extends MessageHandler {
@override
Future<dynamic> handleMessage(dynamic params) async {
return getIt.get<KouyuService>().stopPlay();
}
}
import 'dart:convert';
import 'package:appframe/config/constant.dart';
import 'package:appframe/config/locator.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_plugin_stkouyu/index.dart';
import 'package:shared_preferences/shared_preferences.dart';
enum _KYEvaluateRecordStatus {
notStarted, // 未开始
recording, // 录音中
stopped, // 已停止录音
completed, // 已完成(已出评测结果)
error, // 出错
}
class KouyuService {
// 插件实例
late FlutterPluginSTkouyu _kouyuPlugin;
// 测评状态
_KYEvaluateRecordStatus _evaluateStatus = _KYEvaluateRecordStatus.notStarted;
// 用于保存测评结果
Map<String, dynamic>? _result;
KouyuService() {
_kouyuPlugin = FlutterPluginSTkouyu();
}
Future<void> _initEngine() async {
var engineStatus = await _kouyuPlugin.getEngineStatus();
if (engineStatus == KYEngineStatus.INITIALIZED) {
return;
}
OnInitEngineListener onInitEngineListener = OnInitEngineListener(
onStartInitEngine: () {
debugPrint("初始化引擎");
},
onInitEngineSuccess: () {
debugPrint("初始化引擎成功");
},
onInitEngineFailed: () {
debugPrint("初始化引擎失败");
},
);
String userId = getIt.get<SharedPreferences>().getString('auth_userCode') ?? '';
KYEngineSetting kyEngineSetting = KYEngineSetting();
kyEngineSetting.engineType = "native";
await _kouyuPlugin.initEngine(
Constant.stAppKey,
Constant.stSecretKey,
userId,
kyEngineSetting,
onInitEngineListener,
);
}
Future<void> _onScore(String res) async {
debugPrint('评测结果回调===>');
debugPrint(res);
Map<String, dynamic> result = {};
try {
Map resObj = jsonDecode(res);
if (resObj.containsKey('errId')) {
// 判断是否存在errId字段,有则表示错误回调
result['errId'] = resObj['errId'];
result['error'] = resObj['error'];
} else {
// 此处进行评分结果解析
Map<String, dynamic> resultObj = resObj["result"];
result['score'] = resultObj['overall'];
var recordPath = await _kouyuPlugin.getLastRecordPath();
result['filePath'] = '${Constant.localServerTempFileUrl}$recordPath';
}
} catch (e) {
// 解析JSON失败时的处理
result['errId'] = 0;
result['error'] = '评测结果解析异常';
}
_result = result;
_evaluateStatus = _KYEvaluateRecordStatus.completed;
}
/// 开始评测录音
Future<bool> start(String coreType, String refText) async {
await _initEngine();
if (_evaluateStatus == _KYEvaluateRecordStatus.recording) {
throw Exception("评测状态错误");
}
OnRecordListener onRecordListener = OnRecordListener(
onStart: () {
debugPrint('口语测评,开始录音===>');
_evaluateStatus = _KYEvaluateRecordStatus.recording;
},
onTick: (num var1, num var2) {
// debugPrint('tick===> var1:' + var1.toString() + ", var2:" + var2.toString());
},
onStartRecordFail: (String res) {
debugPrint('口语测评,开始录音失败===>');
_evaluateStatus = _KYEvaluateRecordStatus.error;
},
onRecording: (int vad_status, int sound_intensity) {},
onRecordEnd: () {
debugPrint('口语测评,录音停止===>');
_evaluateStatus = _KYEvaluateRecordStatus.stopped;
},
onScore: _onScore,
);
//评测参数
KYRecordSetting kyRecordSetting = KYRecordSetting();
kyRecordSetting.audioType = "mp3";
kyRecordSetting.request = {
"coreType": coreType,
"refText": refText,
};
_kouyuPlugin.start(kyRecordSetting, onRecordListener).then(
(value) => debugPrint("start result: $value"),
onError: (e) {
debugPrint("start onError: $e");
},
).catchError((e) {
debugPrint("start catch err: $e");
});
return true;
}
/// 停止评测
Future<Map<String, dynamic>> stop() async {
if (_evaluateStatus != _KYEvaluateRecordStatus.recording) {
throw Exception("评测状态错误");
}
await _kouyuPlugin.stop();
// 等待评测结果
while (_evaluateStatus != _KYEvaluateRecordStatus.completed) {
await Future.delayed(Duration(milliseconds: 100));
}
return _result!;
}
/// 取消评测
Future<bool> cancel() async {
if (_evaluateStatus != _KYEvaluateRecordStatus.recording) {
throw Exception("评测状态错误");
}
await _kouyuPlugin.cancel();
_evaluateStatus = _KYEvaluateRecordStatus.notStarted;
return true;
}
/// 回放最后一次评测录音
bool playback() {
OnPlayListener onPlayListener = OnPlayListener(
onStart: () {
debugPrint('回放开始===>');
},
onStartFail: (String str) {
debugPrint('回放失败===>');
},
onEnd: () {
debugPrint('回放结束===>');
},
);
_kouyuPlugin.playback(onPlayListener);
return true;
}
/// 停止回放
bool stopPlay() {
_kouyuPlugin.stopPlay();
return true;
}
/// 释放、销毁
Future<void> dispose() async {
await _kouyuPlugin.deleteEngine();
_kouyuPlugin.dispose();
_evaluateStatus = _KYEvaluateRecordStatus.notStarted;
_result = null;
}
}
......@@ -69,6 +69,8 @@ dependencies:
tencent_cloud_chat_sdk: ^8.7.7201+2
tencent_cloud_chat_push: ^8.7.7201
# --- 声通测评 ---
flutter_plugin_stkouyu: ^1.0.41
# --- 其他硬件功能 ---
geolocator: ^14.0.2
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!