vue项目中集合科大讯飞语音识别功能,web端,语音听写(流式版)WebAPI 文档
科大讯飞官方文档:语音听写(流式版)WebAPI 文档 | 讯飞开放平台文档中心
这次主要是将demo改写成了vue语法,其他的都没变。
先在public文件下加入了iat,也就是demo文件中的dist文件夹
然后还要准备CryptoJS这个第三方的包import CryptoJS from 'crypto-js';,直接npm下载就行。
最后是封装了一个组件,来使用。这个项目使用的是ant的组件库。可自行换成别的。
import { Close, Voice } from '@icon-park/vue-next'; import CryptoJS from 'crypto-js'; import { message } from 'ant-design-vue'; export default { name: 'XfIat', components: { Close, Voice }, data() { return { // 控制录音弹窗 voiceOpen: false, // 是否开始录音 startVoiceStatus: false, // 识别中状态 identifyStatus: false, recorder: null, transcription: '', btnStatus: '', resultText: '', resultTextTemp: '', countdownInterval: null, iatWS: null, recognition: null, }; }, emits: ['sendMsg'], props: { buttonDisabled: { type: Boolean, default: true, }, loading: { type: Boolean, default: false, }, xfIatKeys: { default: { APPID: '', APIKey: '', APISecret: '', }, }, }, methods: { /** * 打开录音 * @returns {Promise} */ async startVoice() { if (this.loading) { return; } this.voiceOpen = true; await this.playIatVoice(); }, async playIatVoice() { if (this.loading) { return; } this.startVoiceStatus = !this.startVoiceStatus; // 浏览器自带的识别 if (this.recognition) { if (this.startVoiceStatus) { this.recognition.start(); } else { this.recognition.stop(); } return; } if (this.startVoiceStatus) { this.connectWebSocket(); } else { this.recorder.stop(); } }, /** * 关闭录音弹窗 */ closeVoiceOpen() { this.voiceOpen = false; this.startVoiceStatus = false; if (this.recorder) { this.recorder.stop(); } if (this.recognition) { this.recognition.stop(); } this.transcription = ''; }, renderResult(resultData) { // 识别结束 const jsonData = JSON.parse(resultData); if (jsonData.data && jsonData.data.result) { const data = jsonData.data.result; let str = ''; const { ws } = data; for (let i = 0; i { console.log('iatWS.onopen', e); // 开始录音 this.recorder.start({ sampleRate: 16000, frameSize: 1280, }); const params = { common: { app_id: this.xfIatKeys.APPID, }, business: { language: 'zh_cn', domain: 'iat', accent: 'mandarin', vad_eos: 5000, dwa: 'wpgs', nbest: 1, wbest: 1, }, data: { status: 0, format: 'audio/L16;rate=16000', encoding: 'raw', }, }; this.iatWS.send(JSON.stringify(params)); }; this.iatWS.onmessage = e => { this.renderResult(e.data); }; this.iatWS.onerror = e => { console.error(e); this.recorder.stop(); this.changeBtnStatus('CLOSED'); }; this.iatWS.onclose = e => { console.log(e); this.recorder.stop(); this.changeBtnStatus('CLOSED'); }; }, getWebSocketUrl() { const { APIKey, APISecret } = this.xfIatKeys; if (!APIKey) { message.error('语音识别配置未生效'); return null; } // 请求地址根据语种不同变化 let url = 'wss://iat-api.xfyun.cn/v2/iat'; const host = 'iat-api.xfyun.cn'; const apiKey = APIKey; const apiSecret = APISecret; const date = new Date().toGMTString(); const algorithm = 'hmac-sha256'; const headers = 'host date request-line'; const signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/iat HTTP/1.1`; const signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret); const signature = CryptoJS.enc.Base64.stringify(signatureSha); const authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`; const authorization = btoa(authorizationOrigin); url = `${url}?authorization=${authorization}&date=${date}&host=${host}`; return url; }, countdown() { let seconds = 60; console.log(`录音中(${seconds}s)`); this.countdownInterval = setInterval(() => { seconds -= 1; if (seconds { this.transcription = Array.from(event.results) .map(result => result[0].transcript) .join(''); }; this.recognition.onerror = event => { console.error('识别错误:', event.error); }; this.recognition.onend = () => { console.log('录音停止了'); this.startVoiceStatus = false; }; this.recognition.onstart = () => { this.changeBtnStatus('OPEN'); }; return; } */ this.recorder = new window.RecorderManager('/iat'); this.recorder.onStart = () => { this.changeBtnStatus('OPEN'); }; this.recorder.onFrameRecorded = ({ isLastFrame, frameBuffer }) => { if (this.iatWS.readyState === this.iatWS.OPEN) { this.iatWS.send( JSON.stringify({ data: { status: isLastFrame ? 2 : 1, format: 'audio/L16;rate=16000', encoding: 'raw', audio: this.toBase64(frameBuffer), }, }), ); if (isLastFrame) { this.changeBtnStatus('CLOSING'); } } }; this.recorder.onStop = () => { console.log('录音结束,停止定时器'); clearInterval(this.countdownInterval); this.startVoiceStatus = false; }; }, }; 语音输入 {{ identifyStatus ? '识别中' : '想问什么,说来听听...' }} 点击下方语音图标可{{ startVoiceStatus ? '停止' : '开始' }}录音 清空 发送 .send-btn { //margin-left: 1rem; height: 2.25rem; width: 2.25rem; cursor: pointer; border-radius: 100%; box-sizing: border-box; display: flex; align-items: center; justify-content: center; flex-shrink: 0; transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 0.15s; &:hover { background-color: #f2f3f5; .icon { margin: 2px 2px 0 0; color: #7f5df6; } } } .inactive { .icon { color: #b4becf; margin: 2px 2px 0 0; } } .active { .icon { margin: 2px 2px 0 0; color: #7f5df6; } } .voice-box { width: 100%; position: fixed; bottom: 5px; left: 0; z-index: 3; display: flex; align-items: center; justify-content: center; .voice-body { position: absolute; bottom: 0; width: 64%; height: 11rem; border-radius: 1rem; padding: 1rem 1.25rem; font-size: .875rem; line-height: 1.5rem; box-shadow: 0 8px 32px rgba(0, 0, 0, .16); box-sizing: border-box; background-color: #fff; border: 1px solid #e4e7ed; color: #303133; display: flex; flex-direction: column; justify-content: space-between; .close-box { position: absolute; right: 1rem; top: 1rem; .close-icon { color: #596780; float: right; cursor: pointer; } } .textarea-box { padding-right: 1.25rem; .custom-textarea-wrapper { max-height: 90px; /* 设置最大高度以激活滚动条 */ overflow: auto; /* 启用滚动条 */ } /* WebKit 浏览器的滚动条样式 */ .custom-textarea-wrapper ::-webkit-scrollbar { width: 8px; /* 设置滚动条的宽度 */ } .custom-textarea-wrapper ::-webkit-scrollbar-track { background: transparent; /* 滚动条轨道背景 */ } .custom-textarea-wrapper ::-webkit-scrollbar-thumb { background: #f2f3f5; /* 滚动条滑块的颜色 */ border-radius: 4px; /* 滚动条滑块的圆角 */ } .custom-textarea-wrapper ::-webkit-scrollbar-thumb:hover { background: #555; /* 滚动条滑块在悬停时的颜色 */ } /* Firefox 的滚动条样式 */ .custom-textarea-wrapper { scrollbar-width: thin; /* 滚动条宽度: thin/auto */ scrollbar-color: #f2f3f5 transparent; /* 滚动条颜色 (滑块 背景) */ } .text-center-box { display: flex; flex-direction: column; align-items: center; justify-content: center; .font-medium { font-weight: 500; } .tip-text { font-size: .75rem; line-height: 1rem; margin-top: 0.35rem; color: #9DA3AF; } } } .action-box { display: flex; align-items: center; justify-content: center; position: relative; gap: 2.5rem; .start-voice-box { width: 2.25rem; height: 2.25rem; display: flex; align-items: center; justify-content: center; background-color: rgb(124 92 252); border-radius: 50%; cursor: pointer; z-index: 3; .start-voice { color: #fff; } } .start-voice-box:hover { opacity: 0.8; } .water-ripples { z-index: 2; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 28px; height: 28px; .circle { width: 28px; height: 28px; background: rgb(124, 92, 252); border-radius: 100%; position: absolute; } .circle1 { animation-delay: 0s; animation-name: waterCircle; animation-duration: 2s; animation-iteration-count: infinite; animation-timing-function: linear; } .circle2 { animation-delay: 1s; animation-name: waterCircle; animation-duration: 2s; animation-iteration-count: infinite; animation-timing-function: linear; } .circle3 { animation-delay: 2s; animation-name: waterCircle; animation-duration: 2s; animation-iteration-count: infinite; animation-timing-function: linear; } @keyframes waterCircle { 0% { transform: scale(1); opacity: .5; } 25% { transform: scale(1.25); opacity: .375; } 50% { transform: scale(1.5); opacity: .25; } 75% { transform: scale(1.75); opacity: .125; } 100% { transform: scale(2); opacity: .05; } } } .ant-btn-text { color: #909399; } .ant-btn-text:disabled { background-color: transparent; border-color: transparent; color: #c8c9cc; } .ant-btn-text:not(:disabled):hover { background-color: transparent; border-color: transparent; color: #c8c9cc; } } } } @media (max-width: 1279px) { .voice-body{ width: 76% !important; } } @media (max-width: 1023px) { .voice-body{ width: calc(100% - 1rem) !important; } }
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。