vue使用websocket语音识别连续流式输出

这篇具有很好参考价值的文章主要介绍了vue使用websocket语音识别连续流式输出。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

首先安装recorder-core依赖

npm i recorder-core

第一部分html,按钮,显示文本

<template>
    <div class="sound">
        <textarea v-model="text" name="" id="" cols="30" rows="10" placeholder="点击按钮开始说话,您的语音将会被转为文本,请允许浏览器获取麦克风权限"></textarea>
        <div class="btn"> 
            <button @click="startSpeechRecognition()">开始识别</button>
        <button @click="closeWebsocket()">停止识别</button>
        </div>
    </div>
</template>

第二部分:record.js文件,录音文件放入utils文件中,在vue页面中引入使用

//Recorder
import Recorder from 'recorder-core'
// type
import 'recorder-core/src/engine/pcm.js'
//可选的扩展支持项
import 'recorder-core/src/extensions/wavesurfer.view.js'

export const jsRecorder = (cb) => {
  var testSampleRate = 16000
  var testBitRate = 16
  var SendFrameSize = 1280
  //  去掉console
  Recorder.CLog = () => {}

  //重置环境,每次开始录音时必须先调用此方法,清理环境
  var RealTimeSendTryReset = function () {
    realTimeSendTryChunks = null
  }

  var realTimeSendTryNumber
  var transferUploadNumberMax
  var realTimeSendTryChunk
  var realTimeSendTryChunks

  //=====实时处理核心函数==========
  var RealTimeSendTry = function (buffers, bufferSampleRate, isClose) {
    if (realTimeSendTryChunks == null) {
      realTimeSendTryNumber = 0
      transferUploadNumberMax = 0
      realTimeSendTryChunk = null
      realTimeSendTryChunks = []
    }
    //配置有效性检查
    if (testBitRate == 16 && SendFrameSize % 2 == 1) {
      return
    }

    var pcm = [],
      pcmSampleRate = 0
    if (buffers.length > 0) {
      //借用SampleData函数进行数据的连续处理,采样率转换是顺带的,得到新的pcm数据
      var chunk = Recorder.SampleData(
        buffers,
        bufferSampleRate,
        testSampleRate,
        realTimeSendTryChunk
      )

      //清理已处理完的缓冲数据,释放内存以支持长时间录音,最后完成录音时不能调用stop,因为数据已经被清掉了
      for (
        var i = realTimeSendTryChunk ? realTimeSendTryChunk.index : 0;
        i < chunk.index - 3;
        i++
      ) {
        buffers[i] = null
      }
      realTimeSendTryChunk = chunk //此时的chunk.data就是原始的音频16位pcm数据(小端LE),直接保存即为16位pcm文件、加个wav头即为wav文件、丢给mp3编码器转一下码即为mp3文件

      pcm = chunk.data
      pcmSampleRate = chunk.sampleRate
      if (pcmSampleRate != testSampleRate)
        //除非是onProcess给的bufferSampleRate低于testSampleRate
        throw new Error(
          '不应该出现pcm采样率' +
            pcmSampleRate +
            '和需要的采样率' +
            testSampleRate +
            '不一致'
        )
    }

    //将pcm数据丢进缓冲,凑够一帧发送,缓冲内的数据可能有多帧,循环切分发送
    if (pcm.length > 0) {
      realTimeSendTryChunks.push({ pcm: pcm, pcmSampleRate: pcmSampleRate })
    }

    //从缓冲中切出一帧数据
    var chunkSize = SendFrameSize / (testBitRate / 8) //8位时需要的采样数和帧大小一致,16位时采样数为帧大小的一半
    pcm = new Int16Array(chunkSize)
    pcmSampleRate = 0
    var pcmOK = false,
      pcmLen = 0
    for1: for (var i1 = 0; i1 < realTimeSendTryChunks.length; i1++) {
      chunk = realTimeSendTryChunks[i1]
      pcmSampleRate = chunk.pcmSampleRate

      for (var i2 = chunk.offset || 0; i2 < chunk.pcm.length; i2++) {
        pcm[pcmLen] = chunk.pcm[i2]
        pcmLen++

        //满一帧了,清除已消费掉的缓冲
        if (pcmLen == chunkSize) {
          pcmOK = true
          chunk.offset = i2 + 1
          for (var i3 = 0; i3 < i1; i3++) {
            realTimeSendTryChunks.splice(0, 1)
          }
          break for1
        }
      }
    }

    //缓冲的数据不够一帧时,不发送 或者 是结束了
    if (!pcmOK) {
      if (isClose) {
        var number = ++realTimeSendTryNumber
        TransferUpload(number, null, 0, null, isClose)
      }
      return
    }

    //16位pcm格式可以不经过mock转码,直接发送new Blob([pcm.buffer],{type:"audio/pcm"}) 但8位的就必须转码,通用起见,均转码处理,pcm转码速度极快
    number = ++realTimeSendTryNumber
    var encStartTime = Date.now()
    var recMock = Recorder({
      type: 'pcm',
      sampleRate: testSampleRate, //需要转换成的采样率
      bitRate: testBitRate //需要转换成的比特率
    })
    recMock.mock(pcm, pcmSampleRate)
    recMock.stop(
      function (blob, duration) {
        blob.encTime = Date.now() - encStartTime

        //转码好就推入传输
        TransferUpload(number, blob, duration, recMock, false)

        //循环调用,继续切分缓冲中的数据帧,直到不够一帧
        RealTimeSendTry([], 0, isClose)
      },
      function (msg) {
        //转码错误?没想到什么时候会产生错误!
      }
    )
  }

  //=====数据传输函数==========
  var TransferUpload = function (
    number,
    blobOrNull,
    duration,
    blobRec,
    isClose
  ) {
    // console.log(number, blobOrNull, duration, blobRec, isClose)
    transferUploadNumberMax = Math.max(transferUploadNumberMax, number)
    if (blobOrNull) {
      var blob = blobOrNull
      var encTime = blob.encTime
      cb(blob, encTime)
    }

    if (isClose) {
      // console.log('isClose')
    }
  }

  //调用录音
  var rec
  var wave
  function recStart() {
    if (rec) {
      rec.close()
    }
    rec = Recorder({
      type: 'unknown',
      onProcess: function (
        buffers,
        powerLevel,
        bufferDuration,
        bufferSampleRate
      ) {
        //推入实时处理,因为是unknown格式,buffers和rec.buffers是完全相同的,只需清理buffers就能释放内存。
        RealTimeSendTry(buffers, bufferSampleRate, false)
        //可视化图形绘制
        // wave.input(buffers[buffers.length - 1], powerLevel, bufferSampleRate);
      }
    })

    // var t = setTimeout(function () {
    //   console.log('无法录音:权限请求被忽略(超时假装手动点击了确认对话框)', 1)
    // }, 8000)

    rec.open(function () {
      //打开麦克风授权获得相关资源
      rec.start() //开始录音
      RealTimeSendTryReset() //重置环境,开始录音时必须调用一次

      //此处创建这些音频可视化图形绘制浏览器支持妥妥的
      // wave = Recorder.WaveSurferView({
      //   elem: ".speak-wave",
      //   height: 30 //显示宽度
      //   , scale: 2 //缩放系数,应为正整数,使用2(3? no!)倍宽高进行绘制,避免移动端绘制模糊
      //   , fps: 50 //绘制帧率,不可过高,50-60fps运动性质动画明显会流畅舒适,实际显示帧率达不到这个值也并无太大影响
      //   , duration: 2500 //当前视图窗口内最大绘制的波形的持续时间,此处决定了移动速率
      //   , direction: 1 //波形前进方向,取值:1由左往右,-1由右往左
      //   , position: 0 //绘制位置,取值-1到1,-1为最底下,0为中间,1为最顶上,小数为百分比
      //   , centerHeight: 1 //中线基础粗细,如果为0不绘制中线,position=±1时应当设为0
      //   //波形颜色配置:[位置,css颜色,...] 位置: 取值0.0-1.0之间
      //   , linear: [0, "rgba(0,187,17,1)", 0.7, "rgba(255,215,0,1)", 1, "rgba(255,102,0,1)"]
      //   , centerColor: "" //中线css颜色,留空取波形第一个渐变颜色 });
      // }, function (msg, isUserNotAllow) {
      //   clearTimeout(t);
      //   console.log((isUserNotAllow ? "UserNotAllow," : "") + "无法录音:" + msg, 1);
      // });
    })
  }
  // close
  function recStop() {
    rec && rec.close() //直接close掉即可,这个例子不需要获得最终的音频文件
    RealTimeSendTry([], 0, true) //最后一次发送
  }

  return {
    recStart: recStart,
    recStop: recStop
  }
}

将record.js引入

<script>
import { jsRecorder } from "@/utils/record.js"; //引入录音文件

export default {
    data() {
        return {
            text: '',//识别文本
            isRecording: false,
            websocket: null,//websocket定义
			recStart:null,//开始录音
			recStop:null,//结束录音
        }
    },
    created() {
        // 使用 jsRecorder 创建一个对象并进行解构操作
		const callback = (blob, encTime) => {
			//发送音频blob
			if (this.websocket && this.isRecording) {
				this.websocket.send(blob)
			}
		}
		const recorder = jsRecorder(callback)
        // 开始录音、结束录音方法
		this.recStart = recorder.recStart
		this.recStop = recorder.recStop
	},
    methods: {
        startSpeechRecognition() {
            // 判断麦克风是否打开
			navigator.mediaDevices.getUserMedia({ audio: true }).then(stream => {
				//调用websocket方法
                // 如果需要加一些图片验证或者其他验证在这里加一些逻辑,成功之后调用websocket
                this.isRecording=true
                // 调用录音方法
                this.recStart()
                this.asrWebSocket()

			}).catch(error => {
                this.$message.error('麦克风未打开!');
				// 麦克风未打开
				switch (error.message || error.name) {
                case 'PERMISSION_DENIED':
                case 'PermissionDeniedError':
                console.info('用户拒绝提供信息。')
                break
                case 'NOT_SUPPORTED_ERROR':
                case 'NotSupportedError':
                console.info('浏览器不支持硬件设备。')
                break
                case 'MANDATORY_UNSATISFIED_ERROR':
                case 'MandatoryUnsatisfiedError':
                console.info('无法发现指定的硬件设备。')
                break
                default:
                console.info('无法打开麦克风。异常信息:' + (error.code || error.name))
                break
            }
			});
        },
        asrWebSocket(){
            //使用后端提供的地址
            let url='wss://open-api.com/asr/test'
            //new 一个websocket
            this.websocket = new WebSocket(url)
            console.log(this.websocket,'new websocket')
            this.websocket.onopen = () => {
                console.log('连接成功')
                //链接成功之后发送websocket请求参数
                this.sendStart()
            }
            // 客户端接收服务端返回的数据
			this.websocket.onmessage = (evt) => {
				if (evt.data) {
					const obj = JSON.parse(evt.data)
					console.log('websocket-asr返回的数据:', obj)
                    //返回的数据看后端返回的是怎么样的,这是我的一些举例
					if(obj.type=='server_error'){
						if(obj.error_msg.code == '10002'){
							//参数缺失
						}else if(obj.error_msg.code == "10003"){
							//鉴权失败,包括非法的appkey,余额不足等情况
						}
					}else if (obj.type == 'partial_result') {
						//实时结果返回 转成文字
						this.text = obj.content + ' '
					}else if (obj.type == 'onebox_header') {
					    // 服务端发送对话结果前,会发送onebox_header通知消息。收到消息后,客户端需要准备对后续收到的websocket binary数据进行gzip解压得到字符串,并解析为json,为本轮对话结果
					} else if (obj.type == 'slience') {
					    // slience server端监测到静音后
					} else if (obj.type == 'speech_end') {
					    //服务端在解码结束后,会发送speech_end,并将最终结果存放在content字段。
					    //监测到end后调用停止方法
                        this.closeWebsocket()
					} else if (obj.type == 'result_end') {
					    // 服务端在本轮结束时,会发送result_end通知消息,客户端close websocket连接
					    // 非连续对话在这返回 continuous_decoding
					} else if (obj.type == 'server_error') {
					    // 当发生异常或输入不合法时,服务器会发送server error消息
					}
				}
			}
            this.websocket.onerror = (evt) => {
				console.error('websocket-asr错误:', evt)
				// todo 重连机制触发
			}
			// 关闭连接
			this.websocket.onclose = (evt) => {
				console.log('websocket-asr关闭:', evt)
			}
        },
        // 请求参数 =====看后端提供的参数信息========
        sendStart(){
            this.websocket.send(JSON.stringify({
                signal: 'start',
                contentType: 'audio/x-wav;codec=pcm;bit=16;rate=16000', //音频格式,支持pcm、wav
                partial_result: 'enable', //是否需要解码中间结果,通常只在实时语音识别场景中使用。
				silence_detection: 'disable', //是否开启服务端静音监测,一般建议开启。
            }))
        },
        // 关闭websocket
        closeWebsocket(){
            //关闭录音
            this.recStop();
            this.isRecording = false
            // websocket关闭参数
            this.websocket.send(
				JSON.stringify({
					signal: 'end'
				})
    		)
        }
    }
}
</script>

css文章来源地址https://www.toymoban.com/news/detail-639181.html

<style lang="less" scoped>
.sound {
    width: 100%;
    height: 140px;
    textarea {
        width: 90%;
        resize: none;
		outline: none;
		border-radius: 8px;
		padding: 0 32px;
        padding-top: 28px;
    }
    .btn{
        display: flex;
    }
    button {
        width: 226px;
        height: 42px;
        background: #3583FB;
        border-radius: 6px;
        border: none;
        font-size: 14px;
        font-family: PingFang-SC-Regular, PingFang-SC;
        font-weight: 400;
        color: #FFFFFF;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        margin: auto;
        cursor: pointer;

    }
}
</style>

到了这里,关于vue使用websocket语音识别连续流式输出的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java调用GPT实现可连续对话和流式输出

    源码及更详细的介绍说明参见Git上的 README.md 文档 https://github.com/asleepyfish/chatgpt 本文Demo(SpringBoot和Main方法Demo均包括)的Git地址:https://github.com/asleepyfish/chatgpt-demo 流式输出结合Vue前端的Demo的Git地址:https://github.com/asleepyfish/chatgpt-vue 后续使用方法和api版本更新均在Github的READM

    2024年02月09日
    浏览(25)
  • 端到端流式语音识别研究综述——语音识别(论文研读)

    语音识别是实现人机交互的一种重要途径,是自然语言处理的基础环节,随着人工智能技术的发展,人机交互等大量应用场景存在着流式语音识别的需求。流式语音识别的定义是一边输入语音一边输出结果,它能够大大减少人机交互过程中语音识别的处理时间。目前在学术研

    2024年02月04日
    浏览(28)
  • 【语音识别】连续语音数字电话按键语音识别附Matlab代码

     ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,代码获取、论文复现及科研仿真合作可私信。 🍎个人主页:Matlab科研工作室 🍊个人信条:格物致知。 更多Matlab完整代码及仿真定制内容点击👇 智能优化算法       神经网络预测       雷达通信       无

    2024年02月21日
    浏览(34)
  • 鸿蒙应用开发-录音并使用WebSocket实现实时语音识别

    功能介绍: 录音并实时获取RAW的音频格式数据,利用WebSocket上传数据到服务器,并实时获取语音识别结果,参考文档使用AudioCapturer开发音频录制功能(ArkTS),更详细接口信息请查看接口文档:AudioCapturer8+和@ohos.net.webSocket (WebSocket连接)。 知识点: 熟悉使用AudioCapturer录音并实时

    2024年04月25日
    浏览(31)
  • 百度语音识别(语音转文字)vue版本 前端(后端需要做个请求转发即可)

    这个项目需要用到语音识别,最后选择的是百度语音识别。原因第一是项目中用到的地方不大,属于微型和小型功能点,第二就是属于临时增加的需求,没有太多的时间去开发,第三就是后端对于自主开发语音识别觉得较为困难,浪费时间。 加载语音识别的文件 下载recorde

    2024年02月12日
    浏览(39)
  • Java调用ChatGPT(基于SpringBoot),实现可连续对话和流式输出的ChatGPT API(可自定义实现AI助手)

    源码及更详细的介绍说明参见Git上的 README.md 文档 https://github.com/asleepyfish/chatgpt 本文Demo(SpringBoot和Main方法Demo均包括)的Git地址:https://github.com/asleepyfish/chatgpt-demo 流式输出结合Vue前端的Demo的Git地址:https://github.com/asleepyfish/chatgpt-vue 后续使用方法和api版本更新均在Github的READM

    2023年04月13日
    浏览(38)
  • 中文连续视觉语音识别挑战赛

    视觉语音识别,也称唇语识别,是一项通过口唇动作来推断发音内容的技术。该技术在公共安全、助老助残、视频验真等领域具有重要应用。当前,唇语识别的研究方兴未艾,虽然在独立词、短语等识别上取得了长足进展,但在大词表连续识别方面仍面临巨大挑战。特别是对

    2024年02月06日
    浏览(31)
  • 前端实现chatGpt流式输出 - SSE

    一、chatGpt流式输出技术分析 在使用ChatGPT时,模型的回复内容是连续输出,而不是整段话直接出现,因为模型需要不断预测接下来要回复什么内容,如果等整段回复生成之后再输出到网页,用户体验就会很差,后面才了解到使用SSE技术可以实现。 相关知识小tips 长轮询:客户

    2024年03月18日
    浏览(35)
  • 【花雕动手做】ASRPRO语音识别(16)---1.8寸彩屏连续显示亮度

    本例实验的SPI彩色液晶1.77寸显示屏(ST7735驱动) 电原理图 SPI彩色液晶显示屏:TFT177-SPI,型号为SX177QQVGA,像素128X160TFT,驱动芯片为ST7735S,这是一款支持SPI接口的1.77寸TFT彩屏,可以显示文字、图形、图片等内容,提高用户互动体验度。 本例实验采用PT0603光敏三极管 电原理图

    2024年02月04日
    浏览(35)
  • Unity 工具 之 Azure 微软连续语音识别ASR的简单整理

    目录 Unity 工具 之 Azure 微软连续语音识别ASR的简单整理 一、简单介绍 二、实现原理 三、注意实现 四、实现步骤  五、关键脚本 Unity 工具类,自己整理的一些游戏开发可能用到的模块,单独独立使用,方便游戏开发。 本节介绍,这里在使用微软的Azure 进行语音合成的两个方

    2024年02月01日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包