vue语音识别,科大讯飞语音识别webapi

语音识别,科大讯飞语音识别webapi相关内容如下
https://www.xfyun.cn/doc/asr/voicedictation/API.html

vue 项目中引入crypto-js,recorder-core

npm install crypto-js
npm install recorder-core

创建vue页面/ui/src/layout/components/voice/index.vue
科大讯飞语音识别的APPID,API_SECRET,API_KEY替换称自己的。

<template>
    <div class="container">
        <div v-if="showCtrlProcessWaveStatus" style="height:100px;width:100%;box-sizing: border-box;display:inline-block;vertical-align:bottom"
             class="ctrlProcessWave"></div>
        <!-- 放一个 <audio ></audio> 播放器,标签名字大写,阻止uniapp里面乱编译 -->
        <AUDIO :class="isPlay?'visible':'hidden'" ref="LogAudioPlayer" style="width:100%"></AUDIO>
        <el-input :class="isShowBase64?'visible':'hidden'" v-model="text" type="textarea" placeholder="语音Base64内容"
                  rows="10" show-word-limit readonly/>
        <el-input :class="isShowIdentifyText?'visible':'hidden'" v-model="identifyText" type="textarea" placeholder="语音识别内容" rows="10"
                  show-word-limit readonly/>
        <div class="buttonContainer">
            <el-button :class="isOpen?'hidden':'visible'" @click="openVoice" type="success" size="small">
                打开语音
            </el-button>
            <el-button :class="isOpen?'visible':'hidden'" @click="closeVoice" type="primary" size="small">
                关闭语音
            </el-button>
        </div>
    </div>
</template>

<script>
    import 'recorder-core'
    import 'recorder-core/src/engine/mp3.js'
    import 'recorder-core/src/engine/mp3-engine.js'
    // import 'recorder-core/recorder.mp3.min.js'
    import 'recorder-core/src/extensions/waveview'
    import CryptoJS from 'crypto-js'

    const APPID = 'xxxxxxxx'
    const API_SECRET = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    const API_KEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

    export default {
        props: {
            openStatus: {
                type: Boolean,
                default: false
            },
            playStatus: {
                type: Boolean,
                default: false
            },
            showCtrlProcessWaveStatus: {
                type: Boolean,
                default: false
            },
            showBase64Status: {
                type: Boolean,
                default: false
            },
            downStatus: {
                type: Boolean,
                default: false
            },
            identifyStatus: {
                type: Boolean,
                default: false
            },
            showIdentifyTextStatus: {
                type: Boolean,
                default: false
            },
        },
        mounted() {
            if (this.$props.openStatus) {
                this.openVoice()
            }
        },
        data() {
            return {
                isOpen: this.openStatus,
                isPlay: this.playStatus,
                isShowCtrlProcessWave: this.showCtrlProcessWaveStatus,
                isShowBase64: this.showBase64Status,
                isDown: this.downStatus,
                isIdentify: this.identifyStatus,
                isShowIdentifyText: this.showIdentifyTextStatus,

                audio: null, // 音频
                text: null, // 存放转换后的文本内容
                identifyText: "", // 存放转换后的文本内容

                audios: [],
                base64s: [],
                type: "mp3",
                bitRate: 16,
                sampleRate: 16000,

                duration: 0,
                durationTxt: "0",
                powerLevel: 0,

                logs: [],

                sendInterval: 300,//发送间隔时长(毫秒),mp3 chunk数据会缓冲,当pcm的累积时长达到这个时长,就会传输发送。这个值在takeoffEncodeChunk实现下,使用0也不会有性能上的影响。
                realTimeSendTryTime: 0,
                realTimeSendTryNumber: null,
                realTimeSendTryBytesChunks: null,
                realTimeSendTryClearPrevBufferIdx: null,
                realTimeSendTryBuffers: null,
                transferUploadNumberMax: null

            }
        },
        methods: {
            openVoice: function () {
                var This = this;
                var rec = this.rec = Recorder({
                    type: This.type
                    , bitRate: +This.bitRate
                    , sampleRate: +This.sampleRate
                    , onProcess: function (buffers, powerLevel, duration, sampleRate, newBufferIdx, asyncEnd) {
                        This.realTimeOnProcessClear(buffers, powerLevel, duration, sampleRate, newBufferIdx, asyncEnd);//实时数据处理,清理内存

                        This.duration = duration;
                        This.durationTxt = This.formatMs(duration, 1);
                        This.powerLevel = powerLevel;

                        if(This.isShowCtrlProcessWave) This.wave.input(buffers[buffers.length - 1], powerLevel, sampleRate);
                    }
                    , takeoffEncodeChunk: function (chunkBytes) {
                        //大于等于60秒自动关闭
                        if (This.duration >= 60000) {
                            This.closeVoice();
                        } else {
                            //接管实时转码,推入实时处理
                            This.realTimeSendTry(chunkBytes, false);
                        }
                    }
                });
                rec.open(function () {
                    This.reclog("已打开:" + This.type + " " + This.sampleRate + "hz " + This.bitRate + "kbps", 2);
                    This.recStart()
                    This.isOpen = true;

                    if(This.isShowCtrlProcessWave) This.wave = Recorder.WaveView({elem: ".ctrlProcessWave"});

                    if (This.$refs && This.$refs.LogAudioPlayer) {
                        var audio = This.$refs.LogAudioPlayer;
                        audio.pause();
                        audio = null;
                        This.audio = audio;
                    }
                    This.text = null;
                    This.identifyText = "";
                    This.base64s = [];

                }, function (msg, isUserNotAllow) {
                    This.reclog((isUserNotAllow ? "UserNotAllow," : "") + "打开失败:" + msg, 1);
                });
            },
            recStart: function () {
                if (!this.rec || !Recorder.IsOpen()) {
                    this.reclog("未打开录音", 1);
                    this.isOpen = false;
                    return;
                }
                this.rec.start();
                this.realTimeSendTryReset();

                var set = this.rec.set;
                this.reclog("录制中:" + set.type + " " + set.sampleRate + "hz " + set.bitRate + "kbps");
            },
            closeVoice: function () {
                this.realTimeSendTry(null,true);
                this.rec.close();
                this.isOpen = false;
            },
            recLast: function () {
                if (!this.recLogLast) {
                    this.reclog("请先录音,然后停止后再播放", 1);
                    return;
                }
                if (this.isPlay) this.recplay(this.recLogLast.idx);
                if (this.isShowBase64) this.recdown64(this.recLogLast.idx);
                if (this.isDown) this.recdown(this.recLogLast.idx);
            },
            recplay: function (idx) {
                var This = this;
                var o = this.logs[this.logs.length - idx - 1];
                o.play = (o.play || 0) + 1;
                var logmsg = function (msg) {
                    o.playMsg = '<span style="color:green">' + o.play + '</span> ' + This.getTime() + " " + msg;
                };
                logmsg("");

                var audio = this.$refs.LogAudioPlayer;
                audio.controls = true;
                if (!(audio.ended || audio.paused)) {
                    audio.pause();
                }
                audio.onerror = function (e) {
                    logmsg('<span style="color:red">播放失败[' + audio.error.code + ']' + audio.error.message + '</span>');
                };
                audio.src = (window.URL || webkitURL).createObjectURL(o.res.blob);
                audio.play();
                This.audio = audio;
            },
            recdown64: function (idx) {
                var This = this;
                var o = this.logs[this.logs.length - idx - 1];
                var reader = new FileReader();
                reader.readAsDataURL(o.res.blob);
                reader.onloadend = function () {
                    var base64 = (/.+;\s*base64\s*,\s*(.+)$/i.exec(reader.result) || [])[1];
                    This.text = base64;
                };
            },
            recdown: function (idx) {
                var This = this;
                var o = this.logs[this.logs.length - idx - 1];
                o.down = (o.down || 0) + 1;

                o = o.res;
                var name = "rec-" + o.duration + "ms-" + (o.rec.set.bitRate || "-") + "kbps-" + (o.rec.set.sampleRate || "-") + "hz." + (o.rec.set.type || (/\w+$/.exec(o.blob.type) || [])[0] || "unknown");
                var downA = document.createElement("A");
                downA.href = (window.URL || webkitURL).createObjectURL(o.blob);
                downA.download = name;
                downA.click();
            },
            formatMs: function (ms, all) {
                var ss = ms % 1000;
                ms = (ms - ss) / 1000;
                var s = ms % 60;
                ms = (ms - s) / 60;
                var m = ms % 60;
                ms = (ms - m) / 60;
                var h = ms;
                var t = (h ? h + ":" : "")
                    + (all || h + m ? ("0" + m).substr(-2) + ":" : "")
                    + (all || h + m + s ? ("0" + s).substr(-2) + "″" : "")
                    + ("00" + ss).substr(-3);
                return t;
            },
            getTime: function () {
                var now = new Date();
                var t = ("0" + now.getHours()).substr(-2)
                    + ":" + ("0" + now.getMinutes()).substr(-2)
                    + ":" + ("0" + now.getSeconds()).substr(-2);
                return t;
            },
            reclog: function (msg, color, res) {
                var obj = {
                    idx: this.logs.length
                    , msg: msg
                    , color: color
                    , res: res

                    , playMsg: ""
                    , down: 0
                    , down64Val: ""
                };
                if (res && res.blob) {
                    this.recLogLast = obj;
                }
                this.logs.splice(0, 0, obj);
            },
            realTimeSendTryReset(){
                this.realTimeSendTryTime=0;
            },
            realTimeOnProcessClear(buffers, powerLevel, duration, sampleRate, newBufferIdx, asyncEnd) {
                if (this.realTimeSendTryTime == 0) {
                    this.realTimeSendTryTime = Date.now();
                    this.realTimeSendTryNumber = 0;
                    this.transferUploadNumberMax = 0;
                    this.realTimeSendTryBytesChunks = [];
                    this.realTimeSendTryClearPrevBufferIdx = 0;
                    this.realTimeSendTryBuffers = [];
                }

                //清理PCM缓冲数据,最后完成录音时不能调用stop,因为数据已经被清掉了
                //这里进行了延迟操作(必须要的操作),只清理上次到现在的buffer
                for (var i = this.realTimeSendTryClearPrevBufferIdx; i < newBufferIdx; i++) {
                    buffers[i] = null;
                }
                this.realTimeSendTryClearPrevBufferIdx = newBufferIdx;

                //备份一下方便后面生成wav
                for (var i = newBufferIdx; i < buffers.length; i++) {
                    this.realTimeSendTryBuffers.push(buffers[i]);
                }
            },
            realTimeSendTry(chunkBytes, isClose) {
                if (chunkBytes) {//推入缓冲再说
                    this.realTimeSendTryBytesChunks.push(chunkBytes);
                }

                var t1 = Date.now();
                if (!isClose && t1 - this.realTimeSendTryTime < this.sendInterval) {
                    return;//控制缓冲达到指定间隔才进行传输
                }
                this.realTimeSendTryTime = t1;
                var number = ++this.realTimeSendTryNumber;


                //缓冲的chunk拼接成一个更长点的
                var len = 0;
                for (var i = 0; i < this.realTimeSendTryBytesChunks.length; i++) {
                    len += this.realTimeSendTryBytesChunks[i].length;
                }
                var chunkData = new Uint8Array(len);
                for (var i = 0, idx = 0; i < this.realTimeSendTryBytesChunks.length; i++) {
                    var chunk = this.realTimeSendTryBytesChunks[i];
                    chunkData.set(chunk, idx);
                    idx += chunk.length;
                }
                this.realTimeSendTryBytesChunks = [];

                //推入传输
                var blob = null, meta = {};
                if (chunkData.length > 0) {//不是空的
                    blob = new Blob([chunkData], {type: "audio/" + this.type});
                    meta = Recorder.mp3ReadMeta([chunkData.buffer], chunkData.length) || {};//读取出这个片段信息
                }
                this.transferUpload(number
                    , blob
                    , meta.duration || 0
                    , {
                        set: {
                            type: this.type
                            , sampleRate: meta.sampleRate
                            , bitRate: meta.bitRate
                        }
                    }
                    , isClose
                );
                var recMock = Recorder({
                    type: this.type
                    , sampleRate: this.sampleRate
                    , bitRate: this.bitRate
                });
                if (this.realTimeSendTryBuffers.length > 0) {
                    var chunk = Recorder.SampleData(this.realTimeSendTryBuffers, this.sampleRate, this.sampleRate);
                    recMock.mock(chunk.data, this.sampleRate);
                    var This = this;
                    recMock.stop(function (blob, duration) {
                        var item = {
                            blob: blob
                            , duration: duration
                            , durationTxt: This.formatMs(duration)
                            , rec: recMock
                        }
                        This.audios.push(item)
                    });
                }
                this.realTimeSendTryBuffers = [];
            },
            transferUpload(number, blobOrNull, c, blobRec, isClose) {
                this.transferUploadNumberMax = Math.max(this.transferUploadNumberMax, number);
                if (blobOrNull) {
                    var blob = blobOrNull;

                    //*********发送:Base64文本发送***************
                    var This = this;
                    var reader = new FileReader();
                    reader.readAsDataURL(blob);
                    reader.onloadend = function () {
                        var base64 = (/.+;\s*base64\s*,\s*(.+)$/i.exec(reader.result) || [])[1];
                        This.base64s.push(base64)
                        This.connectWebSocket()
                    };
                }
                if(isClose){
                    this.mergeAll();
                }
            },
            mergeAll(){
                var bitRate=this.bitRate
                var idx=-1 +1,files=[];
                var This = this;
                var read=function(){
                    var audios = This.audios;
                    var audiosItem = audios[idx++];
                    if(idx>=audios.length){
                        if(!files.length){
                            return;
                        };

                        var rec = audiosItem.rec;
                        This.mp3Merge(files,bitRate,function(fileBytes,duration){
                            var blob = new Blob([fileBytes.buffer],{type:"audio/"+This.type})
                            This.reclog("已录制:", "", {
                                blob: blob
                                , duration: duration
                                , durationTxt: This.formatMs(duration)
                                , rec: rec
                            });
                            This.recLast();
                        });
                        This.audios = [];
                        return;
                    };
                    var reader=new FileReader();
                    reader.onloadend=function(){
                        files.push(new Uint8Array(reader.result));
                        read();
                    };
                    console.log("audiosItem.blob:",audiosItem.blob)
                    reader.readAsArrayBuffer(audiosItem.blob);
                };
                read();
            },
            mp3Merge(fileBytesList,bitRate,True){
                //计算所有文件总长度
                var size=0;
                for(var i=0;i<fileBytesList.length;i++){
                    size+=fileBytesList[i].byteLength;
                };
                //全部直接拼接到一起
                var fileBytes=new Uint8Array(size);
                var pos=0;
                for(var i=0;i<fileBytesList.length;i++){
                    var bytes=fileBytesList[i];
                    fileBytes.set(bytes,pos);
                    pos+=bytes.byteLength;
                };
                //计算合并后的总时长
                var duration=Math.round(size*8/bitRate);
                True(fileBytes,duration);
            },

            // 连接websocket
            connectWebSocket() {
                if(!this.isIdentify) return;
                console.log("连接websocket")
                return this.getWebSocketUrl().then(url => {
                    if (this.webSocket && this.webSocket.readyState === this.webSocket.OPEN) {
                        return
                    } else {
                        if ('WebSocket' in window) {
                            console.log("创建WebSocket")
                            this.webSocket = new WebSocket(url)
                        } else if ('MozWebSocket' in window) {
                            console.log("MozWebSocket")
                            this.webSocket = new MozWebSocket(url)
                        } else {
                            return
                        }

                        this.webSocket.onopen = e => {
                            this.webSocketSend()
                        }
                        this.webSocket.onmessage = e => {
                            this.result(e.data)
                        }
                        this.webSocket.onerror = e => {
                        }
                        this.webSocket.onclose = e => {
                        }
                    }
                })
            },
            getWebSocketUrl() {
                return new Promise((resolve) => {
                    // 请求地址根据语种不同变化
                    var url = 'wss://iat-api.xfyun.cn/v2/iat'
                    var host = 'iat-api.xfyun.cn'
                    var apiKey = API_KEY
                    var apiSecret = API_SECRET
                    var date = new Date().toGMTString()
                    var algorithm = 'hmac-sha256'
                    var headers = 'host date request-line'
                    var signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v2/iat HTTP/1.1`
                    var signatureSha = CryptoJS.HmacSHA256(signatureOrigin, apiSecret)
                    var signature = CryptoJS.enc.Base64.stringify(signatureSha)
                    var authorizationOrigin = `api_key="${apiKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`
                    var authorization = btoa(authorizationOrigin)
                    url = `${url}?authorization=${authorization}&date=${date}&host=${host}`
                    resolve(url)
                })
            },
            // 向webSocket发送数据
            webSocketSend() {
                console.log("webSocketSend:",JSON.stringify(this.webSocket))
                if (this.webSocket.readyState !== this.webSocket.OPEN) {
                    return
                }
                if (this.base64s.length == 0) return;
                this.identifyText = "";
                var index = 0;
                var params
                params =
                    {
                        common: {
                            app_id: APPID,
                        },
                        business: {
                            language: 'zh_cn', //小语种可在控制台--语音听写(流式)--方言/语种处添加试用
                            domain: 'iat',
                            accent: 'mandarin', //中文方言可在控制台--语音听写(流式)--方言/语种处添加试用
                        },
                        data: {
                            status: 0,
                            format: 'audio/L16;rate=16000',
                            encoding: 'lame',
                            audio: this.base64s[index],
                        },
                    }
                console.log("首帧", this.base64s.length,index);
                // console.log("首帧", JSON.stringify(params));
                this.webSocket.send(JSON.stringify(params))
                this.handlerInterval = setInterval(() => {
                    // websocket未连接
                    if (this.webSocket.readyState !== this.webSocket.OPEN) {
                        console.log("websocket未连接:",this.base64s.length,index)
                        this.closeVoice();
                        clearInterval(this.handlerInterval)
                        return
                    }
                    if (this.base64s.length === index) {
                        params =
                            {
                                data: {
                                    status: 2,
                                    format: 'audio/L16;rate=16000',
                                    encoding: 'lame',
                                    audio: '',
                                },
                            }
                        console.log("尾帧", this.base64s.length,index);
                        // console.log("尾帧", JSON.stringify(params));
                        this.webSocket.send(JSON.stringify(params))
                        clearInterval(this.handlerInterval)
                        return false
                    }
                    // 中间帧
                    index++
                    params =
                        {
                            data: {
                                status: 1,
                                format: 'audio/L16;rate=16000',
                                encoding: 'lame',
                                audio: this.base64s[index],
                            },
                        }
                    console.log("中间帧",this.base64s.length,index);
                    // console.log("中间帧",this.base64s.length,index, JSON.stringify(params));
                    this.webSocket.send(JSON.stringify(params))
                }, 40)
            },
            result(resultData) {
                console.log("resultData:", resultData)
                // 识别结束
                let jsonData = JSON.parse(resultData)
                if (jsonData.data && jsonData.data.result) {
                    let data = jsonData.data.result
                    let status = jsonData.data.status
                    let str = ''
                    let ws = data.ws
                    for (let i = 0; i < ws.length; i++) {
                        str = str + ws[i].cw[0].w
                    }
                    console.log("识别的结果为"+this.isOpen+"===>"+status+":", str)
                    if (status === 0) {
                        this.identifyText = str
                    }
                    if (status === 1) {
                        this.identifyText += str
                    }
                    if (status === 2) {
                        this.identifyText += str
                    }
                }
                if (jsonData.code === 0 && jsonData.data.status === 2) {
                    this.webSocket.close()
                }
                if (jsonData.code !== 0) {
                    this.webSocket.close()
                }
            },
        }
    }
</script>
<style lang="scss" scoped>
    .container {
        width: 720px;
        height: 560px;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        height: 100vh;

        .buttonContainer {
            margin-top: 20px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            flex-wrap: wrap;
        }
    }
    /* 隐藏 */
    .hidden {
        display: none;
    }

    /* 显示 */
    .visible {
        display: block;
    }

</style>

在需要引入组件的页面
<template>标签中引入组件标签

<voice :identifyStatus="true" :showIdentifyTextStatus="true"/>

<script>标签中引入组件

import voice from '@/layout/components/voice'

<script>标签中的export default 里面的components里填入voice

components: {
  voice
}
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
根据提供的引用内容,以下是使用Vue实现科大讯飞语音识别功能的步骤: 1. 首先,确保你的项目使用的是Webpack 4及以上版本。科大讯飞语音识别功能需要在Webpack 4+版本下才能正常使用。 2. 在项目中安装crypto-js库。可以使用npm命令进行安装: ```shell npm install crypto-js ``` 3. 在Vue组件中引入所需的库和文件。根据引用中提到的项目结构,你需要引入crypto-js库和其他相关文件。 4. 在Vue组件中实现语音识别功能。你可以使用科大讯飞提供的API进行语音识别。根据引用中提到的WebSocket连接失败的问题,可能是由于授权信息不正确导致的。请确保你在请求WebSocket连接时提供了正确的授权信息。 以下是一个简单的Vue组件示例,演示了如何使用科大讯飞语音识别功能: ```vue <template> <div> <button @click="startRecognition">开始语音识别</button> <div>{{ recognitionResult }}</div> </div> </template> <script> import CryptoJS from 'crypto-js' export default { data() { return { recognitionResult: '' } }, methods: { startRecognition() { // 构造WebSocket连接的URL const url = 'wss://iat-api.xfyun.cn/v2/iat' // 构造授权信息 const apiKey = 'your_api_key' const apiSecret = 'your_api_secret' const date = new Date().toUTCString() const host = 'iat-api.xfyun.cn' 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 authorization = `api_key="${apiKey}", algorithm="hmac-sha256", headers="host date request-line", signature="${signature}"` // 创建WebSocket连接 const socket = new WebSocket(`${url}?authorization=${encodeURIComponent(authorization)}&date=${encodeURIComponent(date)}&host=${encodeURIComponent(host)}`) // 监听WebSocket连接事件 socket.onopen = () => { console.log('WebSocket连接已打开') } // 监听WebSocket消息事件 socket.onmessage = (event) => { console.log('收到消息:', event.data) this.recognitionResult = event.data } // 监听WebSocket关闭事件 socket.onclose = () => { console.log('WebSocket连接已关闭') } // 监听WebSocket错误事件 socket.onerror = (error) => { console.error('WebSocket连接发生错误:', error) } } } } </script> ``` 请注意,上述示例中的`your_api_key`和`your_api_secret`需要替换为你自己的科大讯飞API密钥。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值