import 'dart:convert'; import 'package:crypto/crypto.dart'; import 'package:demo001/plugin/xunfei/audiototext/result_test.dart'; import 'package:intl/intl.dart'; import 'package:web_socket_channel/web_socket_channel.dart'; typedef ResponseCallback = void Function( Xunfei_AudioToText_Result_Text_Item text); class Xunfei_AudioToText { 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 = "iat-api.xfyun.cn"; final String requestUri = "/v2/iat"; WebSocketChannel? _channel; final ResponseCallback onResponse; // 回调函数类型 late Xunfei_AudioToText_Result_Text currtext; // 静态变量保存唯一实例 static Xunfei_AudioToText? _instance; Xunfei_AudioToText._internal({ required this.appId, required this.apiKey, required this.apiSecret, required this.onResponse, // 在构造函数中传递回调 }); // 工厂构造函数 factory Xunfei_AudioToText({ required String appId, required String apiKey, required String apiSecret, required ResponseCallback onResponse, }) { _instance ??= Xunfei_AudioToText._internal( appId: appId, apiKey: apiKey, apiSecret: apiSecret, onResponse: onResponse, ); return _instance!; } // 创建 WebSocket URL String _createUrl() { 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 = _hmacSha256(apiSecret, signatureOrigin); final authorization = base64.encode(utf8.encode( "hmac username=\"$apiKey\", algorithm=\"hmac-sha256\", headers=\"host date request-line\", signature=\"$signature\"")); final queryParams = { "host": host, "date": date, "authorization": authorization, }; final wsUri = 'ws://$host$requestUri?${Uri(queryParameters: queryParams).query}'; return wsUri; } //测试sdk Future start() async { String wsUrl = _createUrl(); await _connect(wsUrl); await Future.delayed(const Duration(seconds: 3)); return; } // 上传音频 Future pushaudio(Stream> audioStream) async { int frameSize = 1280; // 每一帧的音频大小 double interval = 0.04; // 发送音频间隔(单位:s) int status = STATUS_FIRST_FRAME; // 音频的状态信息,标识音频是第一帧,还是中间帧、最后一帧 currtext = Xunfei_AudioToText_Result_Text(); int index = 0; List buffer = []; try { await for (List frame in audioStream) { // 将音频数据添加到 buffer buffer.addAll(frame); while (buffer.length >= frameSize) { List sendFrame = buffer.sublist(0, frameSize); buffer = buffer.sublist(frameSize); // 判断是否读取到足够的帧 if (index + frameSize <= buffer.length) { frame = buffer.sublist(index, index + frameSize); index += frameSize; } else { frame = buffer.sublist(index); index = buffer.length; // 结束 } // 第一帧处理 if (status == STATUS_FIRST_FRAME) { final param = { "common": { "app_id": appId, }, "business": { "language": "zh_cn", "domain": "iat", "accent": "mandarin", }, "data": { "status": status, "format": "audio/L16;rate=16000", "audio": base64Encode(sendFrame), "encoding": "raw", } }; String data = json.encode(param); _channel?.sink.add(data); // print('第一帧已发送...' + data); status = STATUS_CONTINUE_FRAME; } // 中间帧处理 else if (status == STATUS_CONTINUE_FRAME) { final param = { "data": { "status": status, "format": "audio/L16;rate=16000", "audio": base64Encode(sendFrame), "encoding": "raw", } }; String data = json.encode(param); _channel?.sink.add(data); // print('中间帧已发送...'); } // 最后一帧处理 else if (status == STATUS_LAST_FRAME) { final param = { "data": { "status": status, "format": "audio/L16;rate=16000", "audio": base64Encode(sendFrame), "encoding": "raw", } }; // print('最后一帧已发送...'); String data = json.encode(param); _channel?.sink.add(data); break; } // 模拟音频采样间隔 await Future.delayed( Duration(milliseconds: (interval * 1000).toInt())); } } status = STATUS_LAST_FRAME; String data = json.encode({ "data": { "status": status, "format": "audio/L16;rate=16000", "audio": base64Encode([]), "encoding": "raw", } }); _channel?.sink.add(data); } catch (e) { print("push msg: $e"); } print('音频处理完成'); } // 创建WebSocket连接 Future _connect(String url) async { _channel = WebSocketChannel.connect(Uri.parse(url)); _channel?.stream.listen( (message) { onMessage(message); }, onError: (error) { print('连接失败: $error'); }, onDone: () { print('WebSocket 连接已关闭'); }, cancelOnError: true, ); Future.delayed(const Duration(seconds: 1)); } Future onMessage(String message) async {} // 使用SHA-256算法计算HMAC String _hmacSha256(String key, String message) { var keyBytes = utf8.encode(key); // 将密钥转为字节数组 var messageBytes = utf8.encode(message); // 将消息转为字节数组 var hmac = Hmac(sha256, keyBytes); // 创建 HMAC 对象,指定哈希算法和密钥 var digest = hmac.convert(messageBytes); // 计算消息的哈希 return base64.encode(digest.bytes); // 返回 base64 编码的哈希值 } }