|
- //讯飞的翻译任务
- 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<int> rg;
- final List<dynamic> 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<int, XunferTask_Result_Text_Item> tests = {};
- // 输入音频流
- final StreamController<Uint8List> _inputaudioStream =
- StreamController<Uint8List>();
- //输出音频流
- final StreamController<Uint8List> _outputaudioStream =
- StreamController<Uint8List>();
-
- 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<void> _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<void> _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<String, dynamic> _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<void> 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<int> rg = resultMap["rg"] != null
- ? List<int>.from(resultMap["rg"])
- : []; // 默认值为空列表
- List<dynamic> ws = resultMap["ws"] as List<dynamic>;
- 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<int, XunferTask_Result_Text_Item> _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<String, dynamic>;
- var cw = it["cw"] as List<dynamic>;
- for (var ct in cw) {
- resultStr += ct["w"] as String;
- }
- }
- }
- }
- return resultStr;
- }
- return "";
- }
-
- String translateText() {
- return "";
- }
-
- Stream<Uint8List> originalAudio() {
- return _inputaudioStream.stream;
- }
-
- //音频
- Stream<Uint8List> translateAudio() {
- return _outputaudioStream.stream;
- }
- }
|