讯飞语音听写(流式版)开发指南

语音交互大模型的功能越来越受到重视。讯飞语音听写(流式版)为开发者提供了一种高效、准确的语音识别解决方案。本文将基于 Home.vueiat_xfyun.jssparkChat.js 这三个文档,详细阐述讯飞语音听写(流式版)的开发逻辑、涉及的代码,并提供开发说明、文件结构和分析。

开发说明

页面示例

在这里插入图片描述

功能概述

讯飞语音听写(流式版)允许用户通过麦克风输入语音,实时将语音转换为文字。在本项目中,该功能主要应用于聊天界面,用户可以通过语音输入问题,系统将语音转换为文字后发送给后端进行处理。

环境配置

  • 开发环境:Vue.js 3.x、Vite
  • 依赖库@muguilin/xf-voice-dictation 用于实现讯飞语音听写功能

配置步骤

  1. 安装依赖:在项目根目录下执行以下命令安装 @muguilin/xf-voice-dictation
npm install @muguilin/xf-voice-dictation
  1. 配置讯飞 API 信息:在 iat_xfyun.js 文件中,配置讯飞语音听写的 API 信息,包括 APPIDAPIKeyAPISecret
// 讯飞语音识别配置
const xfConfig = {APPID: '6acb09d5',APIKey: '36fb21a7095db0bb***',APISecret: 'MmNhN2VkY2JkMj****',host: 'iat-api.xfyun.cn',path: '/v2/iat'
}

文件结构

主要文件

  • Home.vue:聊天界面组件,包含语音输入按钮和语音识别结果显示区域。
  • iat_xfyun.js:封装讯飞语音听写功能的工具文件,提供创建语音识别实例的工厂函数。
  • sparkChat.js:与后端进行 WebSocket 通信的工具文件,负责将语音识别结果发送给后端。

文件关系

Home.vue 组件引入 iat_xfyun.js 中创建的语音识别实例,当用户点击语音输入按钮时,调用语音识别实例的 start 方法开始录音。识别结果通过 onTextChange 回调函数返回,将结果显示在界面上,并通过 sparkChat.js 发送给后端。

开发逻辑

1. 创建语音识别实例

iat_xfyun.js 文件中,创建一个工厂函数 createVoiceInstance,用于创建语音识别实例。该函数接受一个回调对象作为参数,包含 onStatusChangeonTextChangeonError 三个回调函数。

文件 iat_xfyun.js
import { XfVoiceDictation } from '@muguilin/xf-voice-dictation'// 讯飞语音识别配置
const xfConfig = {APPID: '6acb09d5',APIKey: '36fb21a7095db0bb***',APISecret: 'MmNhN2VkY2JkMj****',host: 'iat-api.xfyun.cn',path: '/v2/iat'
}// 创建语音识别实例的工厂函数
export const createVoiceInstance = (callbacks) => {let instance = new XfVoiceDictation({...xfConfig,    onWillStatusChange: (oldStatus, newStatus) => {console.log('语音识别状态变更:', { oldStatus, newStatus })          },onTextChange: (text) => {    console.log('语音识别结果:', {text: text,textLength: text ? text.length : 0 })callbacks.onTextChange?.(text)},onError: (error) => {console.error('语音识别错误:', error)callbacks.onError?.(error)}})return instance
}

2. 在 Home.vue 中使用语音识别实例

Home.vue 组件中,引入 createVoiceInstance 函数,创建语音识别实例,并绑定到语音输入按钮的点击事件上。

 
<template><!-- ... 其他代码 ... --><div class="input-bar"><input v-model="inputMessage" type="text" class="apple-input" placeholder="输入咨询内容(如:居住证续签)"@keypress.enter="sendInputMessage(false)" autocapitalize="none" autocomplete="off" spellcheck="false"><svg class="input-icon" :class="{ 'recording': isRecording }" viewBox="0 0 24 24" aria-label="语音麦图标"@mousedown="checkLoginBeforeAction(startRecording)" @mouseup="checkLoginBeforeAction(stopBtnRecording)"><pathd="M12 15c1.65 0 3-1.35 3-3V6c0-1.65-1.35-3-3-3S9 4.35 9 6v6c0 1.65 1.35 3 3 3zm5.91-4.56c.08.33.13.67.13 1.01 0 2.49-2.01 4.5-4.5 4.5H13v2.5h2c.55 0 1 .45 1 1s-.45 1-1 1H8c-.55 0-1-.45-1-1s.45-1 1-1h2V16H9.59c-2.49 0-4.5-2.01-4.5-4.5 0-.34.05-.68.13-1.01A2.999 2.999 0 0 1 3 9c0-1.66 1.34-3 3-3h3V4c0-.55.45-1 1-1s1 .45 1 1v2h3c1.66 0 3 1.34 3 3 0 1.28-.81 2.36-1.9 2.73l.01-.17z" /></svg><div v-show="isRecording" class="recording-tip">正在录音...<span class="recording-dots"></span></div><svg class="input-icon sendTxtMsg" viewBox="0 0 24 24" aria-label="发送图标"   @click="sendInputMessage(true)"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" /></svg></div><!-- ... 其他代码 ... -->
</template><script setup>
import { ref, onBeforeUnmount } from 'vue'
import { createVoiceInstance } from '../utils/voice/iat_xfyun'
import { ElMessage } from 'element-plus'const isRecording = ref(false)
const recognizedText = ref('')
let times = null// 创建语音识别实例
const xfVoice = createVoiceInstance({onTextChange: (text) => {if (text && text.length > 0) {const currentTime = Date.now();// 防止操作过于频繁if (currentTime - lastCallTime.value < 3000) {//只要无这段代码message.value.push()会加入一条除了本身语言以外只多了问号或一个点的消息//如:你好 //message数组内会有两条消息://1.你好//2.你好?//如果有这段代码,message数组内只会有一条消息:console.log('操作过于频繁,请等待10秒后再试');                 return;}lastCallTime.value = currentTime;clearTimeout(times);             xfVoice.stop();// 发送识别结果到服务器inputMessage.value = text;user_message.value = text;  tmpMsgArr.value = [];sendInputMessage(true);}},onError: (error) => {if (error.includes('WebSocket')) {ElMessage.error('语音识别连接失败,请检查网络');} else if (error.includes('authorization')) {ElMessage.error('语音识别授权失败,请检查配置');} else {ElMessage.error('语音识别发生错误:' + error);}isRecording.value = false;}
});const startRecording = () => {if (!isRecording.value) {recognizedText.value = '';xfVoice.start();isRecording.value = true;}
}const stopBtnRecording = () => {isRecording.value = false;xfVoice.stop();
}onBeforeUnmount(() => {clearTimeout(times);if (isRecording.value) {xfVoice.stop();}
});
</script>

3. 处理语音识别结果

onTextChange 回调函数中,处理语音识别结果。当识别到有效文本时,停止录音,并将识别结果发送给后端。

4. 错误处理

onError 回调函数中,处理语音识别过程中可能出现的错误,如网络连接失败、授权失败等,并通过 ElMessage 提示用户。

代码分析

iat_xfyun.js

  • 优点:将讯飞语音听写功能封装在一个独立的文件中,提高了代码的可维护性和可复用性。
  • 缺点:配置信息硬编码在文件中,不利于配置的修改和管理。可以考虑将配置信息提取到环境变量中。

Home.vue

  • 优点:在组件中使用语音识别实例,实现了语音输入功能的集成。通过回调函数处理识别结果和错误,代码结构清晰。
  • 缺点:语音输入按钮的样式和交互逻辑可以进一步优化,提高用户体验。

sparkChat.js

// 讯飞星火大模型WebSocket通信模块
import axios from 'axios'
import getSparkConfig from '../sparkConfig'class SparkChatService {constructor(callbacks) {this.websocket = nullthis.isReconnecting = falsethis.reconnectAttempts = 0this.MAX_RECONNECT_ATTEMPTS = 3this.RECONNECT_INTERVAL = 2000// 获取配置const sparkConfig = getSparkConfig()this.APPID = sparkConfig.APPIDthis.APISecret = sparkConfig.APISecretthis.APIKey = sparkConfig.APIKeythis.host = sparkConfig.hostthis.path = sparkConfig.paththis.sparkBaseUrl = sparkConfig.getWebSocketUrl()// 回调函数this.callbacks = callbacks || {}}// 生成鉴权URL所需的日期getAuthorizationDate() {return new Date().toUTCString()}// 生成鉴权URLasync getAuthUrl() {const date = this.getAuthorizationDate()const tmp = `host: ${this.host}\ndate: ${date}\nGET ${this.path} HTTP/1.1`const encoder = new TextEncoder()const key = await window.crypto.subtle.importKey('raw',encoder.encode(this.APISecret),{ name: 'HMAC', hash: 'SHA-256' },false,['sign'])const signature = await window.crypto.subtle.sign('HMAC',key,encoder.encode(tmp))const signatureBase64 = btoa(String.fromCharCode(...new Uint8Array(signature)))const authorization_origin = `api_key="${this.APIKey}", algorithm="hmac-sha256", headers="host date request-line", signature="${signatureBase64}"`const authorization = btoa(authorization_origin)return `${this.sparkBaseUrl}?authorization=${encodeURIComponent(authorization)}&date=${encodeURIComponent(date)}&host=${encodeURIComponent(this.host)}`}// 检查WebSocket连接状态checkWebSocketConnection() {return this.websocket && this.websocket.readyState === WebSocket.OPEN}// 重连WebSocketasync reconnectWebSocket() {if (this.isReconnecting || this.reconnectAttempts >= this.MAX_RECONNECT_ATTEMPTS) returnthis.isReconnecting = truethis.reconnectAttempts++console.log(`尝试重新连接WebSocket (第${this.reconnectAttempts}次)...`)try {await this.connect()this.isReconnecting = falsethis.reconnectAttempts = 0console.log('WebSocket重连成功')} catch (error) {console.error('WebSocket重连失败:', error)this.isReconnecting = falseif (this.reconnectAttempts < this.MAX_RECONNECT_ATTEMPTS) {setTimeout(() => this.reconnectWebSocket(), this.RECONNECT_INTERVAL)} else {console.error('WebSocket重连次数达到上限')this.callbacks.onError?.('网络连接异常,请刷新页面重试')}}}// 建立WebSocket连接async connect() {try {const url = await this.getAuthUrl()this.websocket = new WebSocket(url)this.websocket.onopen = () => {console.log('WebSocket连接已建立')this.isReconnecting = falsethis.reconnectAttempts = 0this.callbacks.onOpen?.()}this.websocket.onmessage = (event) => {const response = JSON.parse(event.data)if (response.header.code === 0) {if (response.payload.choices.text[0].content) {const content = response.payload.choices.text[0].content.replace(/\r?\n/g, '')this.callbacks.onMessage?.(content)}if (response.header.status === 2) {this.callbacks.onComplete?.()}} else {this.callbacks.onError?.(`抱歉,发生错误:${response.header.message}`)}}this.websocket.onerror = (error) => {console.error('WebSocket错误:', error)if (!this.isReconnecting) {this.reconnectWebSocket()}this.callbacks.onError?.(error)}this.websocket.onclose = () => {console.log('WebSocket连接已关闭')if (!this.isReconnecting) {this.reconnectWebSocket()}this.callbacks.onClose?.()}} catch (error) {console.error('连接WebSocket失败:', error)throw error}}// 发送消息async sendMessage(message) {if (!this.checkWebSocketConnection()) {try {await this.reconnectWebSocket()} catch (error) {console.error('重连失败,无法发送消息')throw new Error('网络连接异常,请稍后重试')}}const requestData = {header: {app_id: this.APPID,uid: 'user1'},parameter: {chat: {domain: 'generalv3',temperature: 0.5,max_tokens: 4096}},payload: {message: {text: [{ role: 'user', content: message }]}}}try {this.websocket.send(JSON.stringify(requestData))} catch (error) {console.error('发送消息失败:', error)throw new Error('发送消息失败,请重试')}}// 关闭连接close() {if (this.websocket) {this.websocket.close()}}
}export default SparkChatService

虽然该文件主要负责与后端进行 WebSocket 通信,但在语音听写功能中起到了将识别结果发送给后端的重要作用。可以考虑对该文件进行进一步的封装,提高代码的可维护性。

AIChat

语音听写(流式版)WebAPI 文档帮助文档:

https://www.xfyun.cn/doc/asr/voicedictation/API.html#%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
缺点:

语音听写流式接口,用于1分钟内的即时语音转文字技术,支持实时返回识别结果,达到一边上传音频一边获得识别文本的效果。
整个会话时长最多持续60s,或者超过10s未发送数据,服务端会主动断开连接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/76808.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于kotlin native的C与kotlin互相调用

本文测试环境为ubuntu&#xff0c;没有使用IDE&#xff1b;从基本层面了解kotlin native环境中&#xff0c;C和kotlin的编译&#xff0c;互相调用。 1. kotlin 动态库 1.1 动态库编译 源码文件libktest.kt&#xff1a; //file name:libktest.kt OptIn(kotlin.experimental.…

【教学类-102-02】自制剪纸图案(留白边、沿线剪)02——Python+PS自动化添加虚线边框

背景需求: 01版本实现了对透明背景png图案边界线的扩展,黑线实线描边 【教学类-102-01】自制剪纸图案(留白边、沿线剪)01-CSDN博客文章浏览阅读974次,点赞15次,收藏7次。【教学类-102-01】自制剪纸图案(留白边、沿线剪)01https://blog.csdn.net/reasonsummer/article…

Python-函数参数

1. 参数基础 函数参数是向函数传递数据的主要方式&#xff0c;Python 提供了多种参数传递机制。 基本用法 def greet(name): # name 是形式参数print(f"Hello, {name}!")greet("Alice") # "Alice" 是实际参数使用场景&#xff1a;当函数需要…

《在 Ubuntu 22.04 上安装 CUDA 11.8 和 Anaconda,并配置环境变量》

安装 CUDA 11.8 和 Anaconda 并配置环境变量 在本教程中&#xff0c;我们将介绍如何在 Ubuntu 22.04 上安装 CUDA 11.8 和 Anaconda&#xff0c;并配置相应的环境变量。我们还将配置使用 阿里云镜像源 来加速软件包更新。以下是具体步骤。 步骤 1&#xff1a;更新软件源 首先…

Ubuntu环境基于Ollama部署DeepSeek+Open-Webui实现本地部署大模型-无脑部署

Ollama介绍 Ollama是一款简单好用的模型部署工具,不仅可以部署DeepSeek,市面上开源模型大部分都可以一键部署,这里以DeepSeek为例 官网 DeepSeek 版本硬件要求 安装Ollama 环境 sudo apt update sudo apt install curl sudo apt install lsof1.命令一键安装 在官网点击…

Angular 项目 PDF 批注插件库在线版 API 示例教程

本文章介绍 Angular 项目中 PDF 批注插件库 ElasticPDF 在线版 API 示例教程&#xff0c;API 包含 ① 导出批注后PDF数据&#xff1b;② 导出纯批注 json 数据&#xff1b;③ 加载旧批注&#xff1b;④ 切换文档&#xff1b;⑤ 切换用户&#xff1b;⑥ 清空批注 等数据处理功能…

Spring Boot 中利用 Jasypt 实现数据库字段的透明加密解密

1. 引言 1.1 什么是 Jasypt Jasypt(Java Simplified Encryption)是一个用于简化 Java 应用程序中加密操作的库。 1.2 为什么使用 Jasypt 简化加密操作:提供简单的 API 进行加密和解密。透明加密:自动处理加密和解密过程,无需手动干预。多种加密算法:支持多种加密算法,…

Linux的: /proc/sys/net/ipv6/conf/ 笔记250405

Linux的: /proc/sys/net/ipv6/conf/ /proc/sys/net/ipv6/conf/ 是 Linux 系统中用于 动态配置 IPv6 网络接口参数 的核心目录。它允许针对不同网络接口&#xff08;如 eth0、wlan0&#xff09;或全局设置&#xff08;all&#xff09;调整 IPv6 协议栈的行为。 它通过虚拟文件系…

Spring Cloud 框架为什么能处理高并发

Spring Cloud框架能够有效处理高并发场景&#xff0c;核心在于其微服务架构设计及多组件的协同作用&#xff0c;具体机制如下&#xff1a; 一、分布式架构设计支撑高扩展性 服务拆分与集群部署 Spring Cloud通过微服务拆分将单体系统解耦为独立子服务&#xff0c;每个服务可独…

无人机智慧路灯杆:智慧城市的‘全能助手’

在城市发展的进程中&#xff0c;智慧路灯杆作为智慧城市建设的关键载体&#xff0c;正逐步从传统的照明设备转型为集多种功能于一体的智能基础设施。无人机与智慧路灯杆的创新性融合&#xff0c;为城市管理和服务带来了全新的变革与机遇。 一、无人机智慧路灯杆的功能概述 照…

Libevent UDP开发指南

UDP 与 TCP 的核心区别 无连接:不需要建立/维护连接 不可靠:不保证数据包顺序和到达 高效:头部开销小,没有连接管理负担 支持广播/多播:可以向多个目标同时发送数据 一、基础UDP服务器实现 1. 创建 UDP 套接字 #include <event2/event.h> #include <event2/lis…

基于阿里云可观测产品构建企业级告警体系的通用路径与最佳实践

前言 1.1 日常生活中的告警 任何连续稳定运行的生产系统都离不开有效的监控与报警机制。通过监控&#xff0c;我们可以实时掌握系统和业务的运行状态&#xff1b;而报警则帮助我们及时发现并响应监控指标及业务中的异常情况。 在日常生活中&#xff0c;我们也经常遇到各种各样…

智能多媒体处理流水线——基于虎跃办公API的自动化解决方案

在内容爆炸的时代&#xff0c;多媒体文件处理&#xff08;图片压缩、视频转码、音频降噪&#xff09;已成为内容生产者的日常挑战。本文将演示如何基于虎跃办公的多媒体处理API&#xff0c;构建自动化处理流水线&#xff0c;实现&#xff1a; 批量文件智能分类格式自动转换质量…

01-STM32(介绍、工具准备、新建工程)p1-4

文章目录 工具准备和介绍硬件设备stm32简介和arm简介stm32简介STM32命名规则STM32选型STM32F103C8T6最小系统板引脚定义STM32启动配置STM32最小系统电路ARM简介 软件安装注册器件支持包安装ST-LINK驱动安装USB转串口驱动 新建工程创建stm32工程STM32工程编译和下载型号分类及缩…

【ABAP】REST/HTTP技术(一)

1、概念 1.1、SAP 如何提供 Http Service 如果要将 SAP 应用程序服务器 &#xff08;application server&#xff09;作为 http 服务提供者&#xff0c;需要定义一个类&#xff0c;这个类必须实现 IF_HTTP_EXTENSION 接口。IF_HTTP_EXTENSION 接口只有一个方法 HANDLE_REQUEST。…

[实战] linux驱动框架与驱动开发实战

linux驱动框架与驱动开发实战 Linux驱动框架与驱动开发实战一、Linux驱动框架概述1.1 Linux驱动的分类1.2 Linux驱动的基本框架 二、Linux驱动关键API详解2.1 模块相关API2.2 字符设备驱动API2.3 内存管理API2.4 中断处理API2.5 PCI设备驱动API 三、Xilinx XDMA驱动开发详解3.1…

1. hadoop 集群的常用命令

1.上传文件 1)hadoop fs -put words.txt /path/to/input/ 2)hdfs dfs -put words.txt /path/wc/input/ 2.获取hdfs中的文件 hadoop fs -get /path/wc/input/words.txt 3.合并下载多个文件 hadoop fs -getmerge /path/wc/input/words.txt /path/wc/input/words2.txt 4.查…

Keepalived+LVS+nginx高可用架构

注明&#xff1a;所有软件已经下载好&#xff0c;防火墙和SELinux已经全部关闭 一.搭建NFS 1.服务端 1.创建文件 [rootnfs ~]# mkdir -p /nfs/data 2、修改权限 [rootnfs ~]# chmod orw /nfs/data 3、写配置文件 [rootnfs ~]# cat /etc/exports /nfs/data 192.168.111.118(r…

深度学习处理文本(13)

我们使用基于GRU的编码器和解码器来在Keras中实现这一方法。选择GRU而不是LSTM&#xff0c;会让事情变得简单一些&#xff0c;因为GRU只有一个状态向量&#xff0c;而LSTM有多个状态向量。首先是编码器&#xff0c;如代码清单11-28所示。 代码清单11-28 基于GRU的编码器 fro…

HashMap 底层原理详解

1. 核心数据结构 JDK 1.7 及之前&#xff1a;数组 链表 JDK 1.8 及之后&#xff1a;数组 链表/红黑树&#xff08;链表长度 ≥8 时转红黑树&#xff0c;≤6 时退化为链表&#xff09; // JDK 1.8 的 Node 定义&#xff08;链表节点&#xff09; static class Node<K,V&g…