//讯飞的翻译任务 import 'dart:async'; import 'dart:convert'; import 'dart:ffi'; import 'dart:typed_data'; import 'package:demo001/xunfei/utils.dart'; import 'package:demo001/xunfei/xunfei.dart'; import 'package:intl/intl.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; typedef TaskStateChangeEvent = void Function(ITaskTrans task); // 定义回调函数类型 class XunferTask_Result_Text_Item { final int sn; final String pgs; final List rg; final List ws; XunferTask_Result_Text_Item({ required this.sn, required this.pgs, required this.rg, required this.ws, }); } class XunferTaskTrans implements ITaskTrans { static const int STATUS_FIRST_FRAME = 0; static const int STATUS_CONTINUE_FRAME = 1; static const int STATUS_LAST_FRAME = 2; final String appId; final String apiKey; final String apiSecret; final String host = "ws-api.xf-yun.com"; final String requestUri = "/v1/private/simult_interpretation"; late String url; late WebSocketChannel? _channel; final TaskStateChangeEvent onEvent; // 回调函数类型 bool isconnected = false; int _state = 0; //未连接 1上传语音 2结束语音 3完成任务 //识别数据 final Map tests = {}; // 输入音频流 final StreamController _inputaudioStream = StreamController(); //输出音频流 final StreamController _outputaudioStream = StreamController(); XunferTaskTrans({ required this.appId, required this.apiKey, required this.apiSecret, required this.onEvent, }) { url = _geturl(); _connect(); _startpush(); } //获取链接地址 String _geturl() { final now = DateTime.now(); final date = DateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'").format(now.toUtc()); final signatureOrigin = "host: $host\ndate: $date\nGET $requestUri HTTP/1.1"; // 使用 HmacUtil 计算 HMAC-SHA256 签名 final signature = XunfeiUtils.hmacSha256(apiSecret, signatureOrigin); final authorization = base64.encode(utf8.encode( "api_key=\"$apiKey\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"$signature\"")); final queryParams = { "authorization": authorization, "date": date, "host": host, "serviceId": "simult_interpretation" }; final wsUri = 'ws://$host$requestUri?${Uri(queryParameters: queryParams).query}'; return wsUri; } // 创建WebSocket连接 Future _connect() async { _channel = WebSocketChannel.connect(Uri.parse(url)); _channel?.stream.timeout(Duration(seconds: 10)); //设置超时时间 _channel?.stream.listen( (message) { onMessage(message); }, onError: (error) { isconnected = false; print('连接失败: $error'); }, onDone: () { isconnected = false; print('WebSocket 连接已关闭'); print('Close code: ${_channel?.closeCode}'); print('Close reason: ${_channel?.closeReason}'); }, cancelOnError: true, ); isconnected = true; } // 上传音频 Future _startpush() async { _state = 1; int frameSize = 1280; // 每一帧的音频大小 double interval = 0.04; // 发送音频间隔(单位:s) int status = STATUS_FIRST_FRAME; // 音频的状态信息,标识音频是第一帧,还是中间帧、最后一帧 Uint8List buffer = Uint8List(0); try { await for (Uint8List chunk in _inputaudioStream.stream) { // 将新数据追加到缓存中 buffer = Uint8List.fromList([...buffer, ...chunk]); // 当缓存中的数据足够一帧时,处理并发送 while (buffer.length >= frameSize) { Uint8List frame = buffer.sublist(0, frameSize); // 取出一帧数据 buffer = buffer.sublist(frameSize); // 移除已处理的数据 // 第一帧处理 if (status == STATUS_FIRST_FRAME) { String data = json.encode(_createParams(appId, status, frame)); _channel?.sink.add(data); print('第一帧已发送... $data'); status = STATUS_CONTINUE_FRAME; } // 中间帧处理 else if (status == STATUS_CONTINUE_FRAME) { String data = json.encode(_createParams(appId, status, frame)); _channel?.sink.add(data); print('中间帧已发送... $data'); } // 模拟音频采样间隔 await Future.delayed( Duration(milliseconds: (interval * 1000).round())); } } status = STATUS_LAST_FRAME; String data = json.encode(_createParams(appId, status, buffer)); _channel?.sink.add(data); print('最后一帧已发送... $data'); _state = 2; } catch (e) { print("上传音频数据异常: $e"); } print('音频处理完成'); } //创建参数 Map _createParams( String appId, int status, Uint8List audio) { final param = { "header": { "app_id": appId, "status": status, }, "parameter": { "ist": { "accent": "mandarin", "domain": "ist_ed_open", "language": "zh_cn", "vto": 15000, "eos": 150000 }, "streamtrans": {"from": "cn", "to": "en"}, "tts": { "vcn": "x2_catherine", "tts_results": { "encoding": "raw", "sample_rate": 16000, "channels": 1, "bit_depth": 16, "frame_size": 0 } } }, "payload": { "data": { "audio": base64.encode(audio), "encoding": "raw", "sample_rate": 16000, "seq": 1, "status": status } } }; return param; } // 向流中添加音频数据 void addAudioData(Uint8List data) { _inputaudioStream.add(data); } //接收到翻译结果 Future onMessage(String message) async { try { print("收到的消息:$message"); // 对结果进行解析 var messageMap = json.decode(message); var status = messageMap["header"]["status"]; var sid = messageMap["header"]["sid"]; // 接收到的识别结果写到文本 if (messageMap.containsKey('payload') && messageMap['payload'].containsKey('recognition_results')) { var result = messageMap['payload']['recognition_results']['text']; var asrresult = utf8.decode(base64.decode(result)); addtext(asrresult); print("收到识别回应:${originalText()}"); } //接收到的翻译结果写到文本 if (messageMap.containsKey('payload') && messageMap['payload'].containsKey('streamtrans_results')) { var result = messageMap['payload']['streamtrans_results']['text']; var transresult = utf8.decode(base64.decode(result)); print("收到翻译结果:$transresult"); } if (messageMap.containsKey('payload') && messageMap['payload'].containsKey('tts_results')) { var audio = messageMap['payload']['tts_results']['audio']; var audioData = base64.decode(audio); _outputaudioStream.add(audioData); print("收到音频结果:${audioData.length}"); } if (status == 2) { print("任务已结束!"); _state = 3; onEvent(this); await Future.delayed(Duration(seconds: 1)); _channel?.sink.close(); } } catch (e) { print("接受结果异常: $e"); } } // 关闭流并停止上传任务 void endpuish() { _inputaudioStream.close(); // 关闭流 } void addtext(String result) { var resultMap = json.decode(result); int sn = resultMap["sn"] as int; String pgs = resultMap["pgs"] as String; List rg = resultMap["rg"] != null ? List.from(resultMap["rg"]) : []; // 默认值为空列表 List ws = resultMap["ws"] as List; var item = XunferTask_Result_Text_Item(sn: sn, pgs: pgs, rg: rg, ws: ws); tests[sn] = item; } int state() { return this._state; } //文字 String originalText() { if (tests.isNotEmpty) { String resultStr = ""; Map _results = {}; var sortedKeys = tests.keys.toList()..sort(); for (var key in sortedKeys) { var item = tests[key]; if (item != null) { if (item.pgs == "rpl") { var start = item.rg[0]; var end = item.rg[1]; for (int i = start; i <= end; i++) { _results.remove(i); } } _results[item.sn] = item; } } var keys = _results.keys.toList()..sort(); for (var key in keys) { var item = tests[key]; if (item != null) { for (var ws in item.ws) { var it = ws as Map; var cw = it["cw"] as List; for (var ct in cw) { resultStr += ct["w"] as String; } } } } return resultStr; } return ""; } String translateText() { return ""; } Stream originalAudio() { return _inputaudioStream.stream; } //音频 Stream translateAudio() { return _outputaudioStream.stream; } }