You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

210 lines
6.4 KiB

  1. import 'dart:convert';
  2. import 'package:crypto/crypto.dart';
  3. import 'package:demo001/plugin/xunfei/audiototext/result_test.dart';
  4. import 'package:intl/intl.dart';
  5. import 'package:web_socket_channel/web_socket_channel.dart';
  6. typedef ResponseCallback = void Function(
  7. Xunfei_AudioToText_Result_Text_Item text);
  8. class Xunfei_AudioToText {
  9. static const int STATUS_FIRST_FRAME = 0;
  10. static const int STATUS_CONTINUE_FRAME = 1;
  11. static const int STATUS_LAST_FRAME = 2;
  12. final String appId;
  13. final String apiKey;
  14. final String apiSecret;
  15. final String host = "iat-api.xfyun.cn";
  16. final String requestUri = "/v2/iat";
  17. WebSocketChannel? _channel;
  18. final ResponseCallback onResponse; // 回调函数类型
  19. late Xunfei_AudioToText_Result_Text currtext;
  20. // 静态变量保存唯一实例
  21. static Xunfei_AudioToText? _instance;
  22. Xunfei_AudioToText._internal({
  23. required this.appId,
  24. required this.apiKey,
  25. required this.apiSecret,
  26. required this.onResponse, // 在构造函数中传递回调
  27. });
  28. // 工厂构造函数
  29. factory Xunfei_AudioToText({
  30. required String appId,
  31. required String apiKey,
  32. required String apiSecret,
  33. required ResponseCallback onResponse,
  34. }) {
  35. _instance ??= Xunfei_AudioToText._internal(
  36. appId: appId,
  37. apiKey: apiKey,
  38. apiSecret: apiSecret,
  39. onResponse: onResponse,
  40. );
  41. return _instance!;
  42. }
  43. // 创建 WebSocket URL
  44. String _createUrl() {
  45. final now = DateTime.now();
  46. final date =
  47. DateFormat("EEE, dd MMM yyyy HH:mm:ss 'GMT'").format(now.toUtc());
  48. final signatureOrigin =
  49. "host: $host\ndate: $date\nGET $requestUri HTTP/1.1";
  50. // 使用 HmacUtil 计算 HMAC-SHA256 签名
  51. final signature = _hmacSha256(apiSecret, signatureOrigin);
  52. final authorization = base64.encode(utf8.encode(
  53. "hmac username=\"$apiKey\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"$signature\""));
  54. final queryParams = {
  55. "host": host,
  56. "date": date,
  57. "authorization": authorization,
  58. };
  59. final wsUri =
  60. 'ws://$host$requestUri?${Uri(queryParameters: queryParams).query}';
  61. return wsUri;
  62. }
  63. //测试sdk
  64. Future<void> start() async {
  65. String wsUrl = _createUrl();
  66. await _connect(wsUrl);
  67. await Future.delayed(const Duration(seconds: 3));
  68. return;
  69. }
  70. // 上传音频
  71. Future<void> pushaudio(Stream<List<int>> audioStream) async {
  72. int frameSize = 1280; // 每一帧的音频大小
  73. double interval = 0.04; // 发送音频间隔(单位:s)
  74. int status = STATUS_FIRST_FRAME; // 音频的状态信息,标识音频是第一帧,还是中间帧、最后一帧
  75. currtext = Xunfei_AudioToText_Result_Text();
  76. int index = 0;
  77. List<int> buffer = [];
  78. try {
  79. await for (List<int> frame in audioStream) {
  80. // 将音频数据添加到 buffer
  81. buffer.addAll(frame);
  82. while (buffer.length >= frameSize) {
  83. List<int> sendFrame = buffer.sublist(0, frameSize);
  84. buffer = buffer.sublist(frameSize);
  85. // 判断是否读取到足够的帧
  86. if (index + frameSize <= buffer.length) {
  87. frame = buffer.sublist(index, index + frameSize);
  88. index += frameSize;
  89. } else {
  90. frame = buffer.sublist(index);
  91. index = buffer.length; // 结束
  92. }
  93. // 第一帧处理
  94. if (status == STATUS_FIRST_FRAME) {
  95. final param = {
  96. "common": {
  97. "app_id": appId,
  98. },
  99. "business": {
  100. "language": "zh_cn",
  101. "domain": "iat",
  102. "accent": "mandarin",
  103. },
  104. "data": {
  105. "status": status,
  106. "format": "audio/L16;rate=16000",
  107. "audio": base64Encode(sendFrame),
  108. "encoding": "raw",
  109. }
  110. };
  111. String data = json.encode(param);
  112. _channel?.sink.add(data);
  113. // print('第一帧已发送...' + data);
  114. status = STATUS_CONTINUE_FRAME;
  115. }
  116. // 中间帧处理
  117. else if (status == STATUS_CONTINUE_FRAME) {
  118. final param = {
  119. "data": {
  120. "status": status,
  121. "format": "audio/L16;rate=16000",
  122. "audio": base64Encode(sendFrame),
  123. "encoding": "raw",
  124. }
  125. };
  126. String data = json.encode(param);
  127. _channel?.sink.add(data);
  128. // print('中间帧已发送...');
  129. }
  130. // 最后一帧处理
  131. else if (status == STATUS_LAST_FRAME) {
  132. final param = {
  133. "data": {
  134. "status": status,
  135. "format": "audio/L16;rate=16000",
  136. "audio": base64Encode(sendFrame),
  137. "encoding": "raw",
  138. }
  139. };
  140. // print('最后一帧已发送...');
  141. String data = json.encode(param);
  142. _channel?.sink.add(data);
  143. break;
  144. }
  145. // 模拟音频采样间隔
  146. await Future.delayed(
  147. Duration(milliseconds: (interval * 1000).toInt()));
  148. }
  149. }
  150. status = STATUS_LAST_FRAME;
  151. String data = json.encode({
  152. "data": {
  153. "status": status,
  154. "format": "audio/L16;rate=16000",
  155. "audio": base64Encode([]),
  156. "encoding": "raw",
  157. }
  158. });
  159. _channel?.sink.add(data);
  160. } catch (e) {
  161. print("push msg: $e");
  162. }
  163. print('音频处理完成');
  164. }
  165. // 创建WebSocket连接
  166. Future<void> _connect(String url) async {
  167. _channel = WebSocketChannel.connect(Uri.parse(url));
  168. _channel?.stream.listen(
  169. (message) {
  170. onMessage(message);
  171. },
  172. onError: (error) {
  173. print('连接失败: $error');
  174. },
  175. onDone: () {
  176. print('WebSocket 连接已关闭');
  177. },
  178. cancelOnError: true,
  179. );
  180. Future.delayed(const Duration(seconds: 1));
  181. }
  182. Future<void> onMessage(String message) async {}
  183. // 使用SHA-256算法计算HMAC
  184. String _hmacSha256(String key, String message) {
  185. var keyBytes = utf8.encode(key); // 将密钥转为字节数组
  186. var messageBytes = utf8.encode(message); // 将消息转为字节数组
  187. var hmac = Hmac(sha256, keyBytes); // 创建 HMAC 对象,指定哈希算法和密钥
  188. var digest = hmac.convert(messageBytes); // 计算消息的哈希
  189. return base64.encode(digest.bytes); // 返回 base64 编码的哈希值
  190. }
  191. }