Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.
 
 
 
 
 
 

342 rindas
10 KiB

  1. //讯飞的翻译任务
  2. import 'dart:async';
  3. import 'dart:convert';
  4. import 'dart:ffi';
  5. import 'dart:typed_data';
  6. import 'package:audioplayers/audioplayers.dart';
  7. import 'package:demo001/xunfei/utils.dart';
  8. import 'package:demo001/xunfei/xunfei.dart';
  9. import 'package:intl/intl.dart';
  10. import 'package:web_socket_channel/web_socket_channel.dart';
  11. class XunferTask_Result_Text_Item {
  12. final int sn;
  13. final String pgs;
  14. final List<int> rg;
  15. final List<dynamic> ws;
  16. XunferTask_Result_Text_Item({
  17. required this.sn,
  18. required this.pgs,
  19. required this.rg,
  20. required this.ws,
  21. });
  22. }
  23. class XunferTaskTrans implements ITaskTrans {
  24. static const int STATUS_FIRST_FRAME = 0;
  25. static const int STATUS_CONTINUE_FRAME = 1;
  26. static const int STATUS_LAST_FRAME = 2;
  27. final String appId;
  28. final String apiKey;
  29. final String apiSecret;
  30. final String host = "ws-api.xf-yun.com";
  31. final String requestUri = "/v1/private/simult_interpretation";
  32. late String url;
  33. late WebSocketChannel? _channel;
  34. bool isconnected = false;
  35. // 数据流控制器
  36. final StreamController<List<int>> _streamController =
  37. StreamController<List<int>>();
  38. // 数据流
  39. Stream<List<int>> get stream => _streamController.stream;
  40. // 是否正在运行
  41. bool _isRunning = false;
  42. // 是否流已关闭
  43. bool _isStreamClosed = false;
  44. // 上传任务的 Future
  45. Future<void>? _uploadTask;
  46. final Map<int, XunferTask_Result_Text_Item> tests = {};
  47. final StreamController<Uint8List> _transtreamController =
  48. StreamController<Uint8List>();
  49. bool _isPlaying = false;
  50. XunferTaskTrans({
  51. required this.appId,
  52. required this.apiKey,
  53. required this.apiSecret,
  54. }) {
  55. url = _geturl();
  56. _connect();
  57. _startUploadTask();
  58. }
  59. //获取链接地址
  60. String _geturl() {
  61. final now = DateTime.now();
  62. final date =
  63. DateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'").format(now.toUtc());
  64. final signatureOrigin =
  65. "host: $host\ndate: $date\nGET $requestUri HTTP/1.1";
  66. // 使用 HmacUtil 计算 HMAC-SHA256 签名
  67. final signature = XunfeiUtils.hmacSha256(apiSecret, signatureOrigin);
  68. final authorization = base64.encode(utf8.encode(
  69. "api_key=\"$apiKey\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"$signature\""));
  70. final queryParams = {
  71. "authorization": authorization,
  72. "date": date,
  73. "host": host,
  74. "serviceId": "simult_interpretation"
  75. };
  76. final wsUri =
  77. 'ws://$host$requestUri?${Uri(queryParameters: queryParams).query}';
  78. return wsUri;
  79. }
  80. //创建参数
  81. Map<String, dynamic> _createParams(
  82. String appId, int status, List<int> audio) {
  83. final param = {
  84. "header": {
  85. "app_id": appId,
  86. "status": status,
  87. },
  88. "parameter": {
  89. "ist": {
  90. "accent": "mandarin",
  91. "domain": "ist_ed_open",
  92. "language": "zh_cn",
  93. "vto": 15000,
  94. "eos": 150000
  95. },
  96. "streamtrans": {"from": "cn", "to": "en"},
  97. "tts": {
  98. "vcn": "x2_catherine",
  99. "tts_results": {
  100. "encoding": "raw",
  101. "sample_rate": 16000,
  102. "channels": 1,
  103. "bit_depth": 16,
  104. "frame_size": 0
  105. }
  106. }
  107. },
  108. "payload": {
  109. "data": {
  110. "audio": base64.encode(audio),
  111. "encoding": "raw",
  112. "sample_rate": 16000,
  113. "seq": 1,
  114. "status": status
  115. }
  116. }
  117. };
  118. return param;
  119. }
  120. // 创建WebSocket连接
  121. Future<void> _connect() async {
  122. _channel = WebSocketChannel.connect(Uri.parse(url));
  123. _channel?.stream.listen(
  124. (message) {
  125. onMessage(message);
  126. },
  127. onError: (error) {
  128. isconnected = false;
  129. print('连接失败: $error');
  130. },
  131. onDone: () {
  132. isconnected = false;
  133. print('WebSocket 连接已关闭');
  134. },
  135. cancelOnError: true,
  136. );
  137. isconnected = true;
  138. }
  139. // 启动上传任务
  140. void _startUploadTask() {
  141. _isRunning = true;
  142. _uploadTask = _pushaudio();
  143. }
  144. // 上传音频
  145. Future<void> _pushaudio() async {
  146. int frameSize = 1280; // 每一帧的音频大小
  147. double interval = 0.04; // 发送音频间隔(单位:s)
  148. int status = STATUS_FIRST_FRAME; // 音频的状态信息,标识音频是第一帧,还是中间帧、最后一帧
  149. int index = 0;
  150. List<int> buffer = [];
  151. try {
  152. await for (List<int> frame in stream) {
  153. // 将音频数据添加到 buffer
  154. buffer.addAll(frame);
  155. while (buffer.length >= frameSize) {
  156. List<int> sendFrame = buffer.sublist(0, frameSize);
  157. buffer = buffer.sublist(frameSize);
  158. // 判断是否读取到足够的帧
  159. if (index + frameSize <= buffer.length) {
  160. frame = buffer.sublist(index, index + frameSize);
  161. index += frameSize;
  162. } else {
  163. frame = buffer.sublist(index);
  164. index = buffer.length; // 结束
  165. }
  166. // 第一帧处理
  167. if (status == STATUS_FIRST_FRAME) {
  168. String data = json.encode(_createParams(appId, status, sendFrame));
  169. _channel?.sink.add(data);
  170. print('第一帧已发送...$data');
  171. status = STATUS_CONTINUE_FRAME;
  172. }
  173. // 中间帧处理
  174. else if (status == STATUS_CONTINUE_FRAME) {
  175. String data = json.encode(_createParams(appId, status, sendFrame));
  176. _channel?.sink.add(data);
  177. // print('中间帧已发送...');
  178. }
  179. // 最后一帧处理
  180. else if (status == STATUS_LAST_FRAME) {
  181. print('最后一帧已发送...');
  182. String data = json.encode(_createParams(appId, status, sendFrame));
  183. _channel?.sink.add(data);
  184. break;
  185. }
  186. // 模拟音频采样间隔
  187. await Future.delayed(
  188. Duration(milliseconds: (interval * 1000).toInt()));
  189. }
  190. }
  191. print('最后一帧已发送...');
  192. status = STATUS_LAST_FRAME;
  193. String data = json.encode(_createParams(appId, status, []));
  194. _channel?.sink.add(data);
  195. } catch (e) {
  196. print("push msg: $e");
  197. }
  198. print('音频处理完成');
  199. }
  200. // 向流中添加音频数据
  201. void addAudioData(List<int> data) {
  202. if (!_isStreamClosed) {
  203. _streamController.add(data);
  204. } else {
  205. print("Stream is closed. Cannot add more data.");
  206. }
  207. }
  208. //接收到翻译结果
  209. Future<void> onMessage(String message) async {
  210. // try {
  211. // print("收到的消息:$message");
  212. // } catch (e) {
  213. // print("receive msg, but parse exception: $e");
  214. // }
  215. // 对结果进行解析
  216. var messageMap = json.decode(message);
  217. var status = messageMap["header"]["status"];
  218. var sid = messageMap["header"]["sid"];
  219. // 接收到的识别结果写到文本
  220. if (messageMap.containsKey('payload') &&
  221. messageMap['payload'].containsKey('recognition_results')) {
  222. var result = messageMap['payload']['recognition_results']['text'];
  223. var asrresult = utf8.decode(base64.decode(result));
  224. addtext(asrresult);
  225. print("收到识别回应..${text()}");
  226. }
  227. if (messageMap.containsKey('payload') &&
  228. messageMap['payload'].containsKey('tts_results')) {
  229. var audio = messageMap['payload']['tts_results']['audio'];
  230. var audioData = base64.decode(audio);
  231. _transtreamController.add(audioData);
  232. // curraudio.addAudioData(audioData);
  233. // var file = File('output/audio/trans.pcm');
  234. // await file.writeAsBytes(audioData, mode: FileMode.append);
  235. }
  236. if (status == 2) {
  237. print("数据处理完毕,等待实时转译结束!同传后的音频文件请到output/audio/目录查看...");
  238. await Future.delayed(Duration(seconds: 3));
  239. close();
  240. }
  241. }
  242. // 关闭流并停止上传任务
  243. Future<void> close() async {
  244. if (!_isStreamClosed) {
  245. _isStreamClosed = true;
  246. _isRunning = false; // 停止上传任务
  247. await _streamController.close(); // 关闭流
  248. await _uploadTask; // 等待上传任务完成
  249. print("Stream and upload task closed.");
  250. }
  251. }
  252. void addtext(String result) {
  253. print("添加文本结果:$result");
  254. var resultMap = json.decode(result);
  255. int sn = resultMap["sn"] as int;
  256. String pgs = resultMap["pgs"] as String;
  257. List<int> rg = resultMap["rg"] != null
  258. ? List<int>.from(resultMap["rg"])
  259. : []; // 默认值为空列表
  260. List<dynamic> ws = resultMap["ws"] as List<dynamic>;
  261. var item = XunferTask_Result_Text_Item(sn: sn, pgs: pgs, rg: rg, ws: ws);
  262. tests[sn] = item;
  263. }
  264. String text() {
  265. if (tests.isNotEmpty) {
  266. String resultStr = "";
  267. Map<int, XunferTask_Result_Text_Item> _results = {};
  268. var sortedKeys = tests.keys.toList()..sort();
  269. for (var key in sortedKeys) {
  270. var item = tests[key];
  271. if (item != null) {
  272. if (item.pgs == "rpl") {
  273. var start = item.rg[0];
  274. var end = item.rg[1];
  275. for (int i = start; i <= end; i++) {
  276. _results.remove(i);
  277. }
  278. }
  279. _results[item.sn] = item;
  280. }
  281. }
  282. var keys = _results.keys.toList()..sort();
  283. for (var key in keys) {
  284. var item = tests[key];
  285. if (item != null) {
  286. for (var ws in item.ws) {
  287. var it = ws as Map<String, dynamic>;
  288. var cw = it["cw"] as List<dynamic>;
  289. for (var ct in cw) {
  290. resultStr += ct["w"] as String;
  291. }
  292. }
  293. }
  294. }
  295. return resultStr;
  296. }
  297. return "";
  298. }
  299. Future<void> audio(AudioPlayer _audioPlayer) async {
  300. _streamController.stream.listen((List<int> data) async {
  301. // 转换为 Uint8List
  302. Uint8List audioBytes = Uint8List.fromList(data);
  303. if (!_isPlaying) {
  304. // 第一次播放
  305. await _audioPlayer.play(BytesSource(audioBytes));
  306. setState(() {
  307. _isPlaying = true;
  308. });
  309. } else {
  310. // 追加数据(需确认插件是否支持动态追加)
  311. // 注意:audioplayers 插件可能不支持此操作!
  312. await _audioPlayer.add(BytesSource(audioBytes));
  313. }
  314. }, onError: (error) {
  315. print("Error in audio stream: $error");
  316. });
  317. }
  318. }