基于阿里云微信小程序语音识别

页面效果
在这里插入图片描述
其中采用阿里云语音识别:阿里云一句话语音识别

语音识别页面


<template>
    <view>
        <view class="chat_list">
            <view v-for="v in chatList" :class="v.type == 'right' ? 'type_right' : 'type_left'">
                <chat :text="v.result" :type="v.type"></chat>
            </view>
        </view>
        <view :class="longPress == '1' ? 'record-layer' : 'record-layer1'">
            <view :class="longPress == '1' ? 'record-box' : 'record-box1'">
                <view class="record-btn-layer flex_row">
                    <button v-show="longPress == '1'" class="record-btn-cir" @click="isKeyWord = !isKeyWord">
                        <image v-if="!isKeyWord" :src="keyword" style=" margin-top: -8rpx;" />
                        <image v-else :src="record" style=" margin-top: -8rpx;" />
                    </button>
                    <button v-show="!isKeyWord" class="record-btn"
                        :class="longPress == '1' ? 'record-btn-1' : 'record-btn-2'"
                        :style="VoiceTitle != '松开手指,取消发送' && longPress != '1' ? 'background-image: linear-gradient(to top, #cfd9df 0%, #e2ebf0 100%);' : 'background-color: rgba(0, 0, 0, .5);color:white'"
                        @longpress="longpressBtn" @touchend="touchendBtn()" @touchmove="handleTouchMove">
                        <image :src="record" />
                        <text>{{ VoiceText }}</text>
                    </button>
                    <u--input v-if="longPress == '1' && isKeyWord" shape="circle" customStyle="u_input" clearable
                        placeholder="请输入内容..." @confirm="confirmMsg"></u--input>
                </view>
                <!-- 语音音阶动画 -->
                <view :class="VoiceTitle != '松开手指,取消发送' ? 'prompt-layer prompt-layer-1' : 'prompt-layer1 prompt-layer-1'"
                    v-if="longPress == '2'">
                    <view class="prompt-loader">
                        <view class="em" v-for="(item, index) in 15" :key="index"></view>
                    </view>
                    <text class="span">{{ VoiceTitle }}</text>
                </view>
            </view>
        </view>
    </view>
</template>

<script>
const recorderManager = uni.getRecorderManager()
const SpeechRecognition = require("../../utils/sr")
const getToken = require("../../utils/token").getToken
import keyword from '../public/images/keyword.png'
import record from '../public/images/record.png'
import chat from './com/chat.vue'
export default {
    components: { chat },
    data() {
        return {
            record,
            keyword,
            longPress: '1', // 1显示 按住 说话 2显示 说话中
            delShow: false, // 删除提示框显示隐藏
            time: 0, //录音时长
            duration: 60000, //录音最大值ms 60000/1分钟
            tempFilePath: '', //音频路径
            startPoint: {}, //记录长按录音开始点信息,用于后面计算滑动距离。
            sendLock: true, //发送锁,当为true时上锁,false时解锁发送
            VoiceTitle: '松手结束录音',
            VoiceText: '按住 说话',
            token: "",
            srStart: false,
            srResult: {},
            sr: null,
            isKeyWord: false,
            chatList: []
        }
    },
    async onLoad() {
        recorderManager.onFrameRecorded((res) => {
            if (this.sr && this.srStart) {
                if (res.frameBuffer) {
                    console.log("send " + res.frameBuffer.byteLength)
                    this.sr.sendAudio(res.frameBuffer)
                }
            }
        })
        recorderManager.onStop(async (res) => {
            if (this.sendLock) {
                //上锁不发送
            } else {//解锁发送,发送网络请求
                if (res.duration < 1000) {
                    wx.showToast({
                        title: "录音时间太短",
                        icon: "none",
                        duration: 1000
                    });
                    await this.sr.close()
                    this.srStart = false
                }
                else {
                    // this.tempFilePath = res.tempFilePath
                    await this.sr.close()
                    this.srStart = false
                    this.srResult.payload.type = 'right'
                    this.chatList.push(this.srResult.payload)
                    console.log('this.chatList.', this.chatList);
                }
            }
        })
        try {
            let token = await getToken('your akid','your akkey')
            this.token = token
        } catch (e) {
            console.log("error on get token:", JSON.stringify(e))
        }
    },
    onUnload: function () {
        this.srStart = false
        recorderManager.stop()
        if (this.sr) {
            this.sr.shutdown()
        }
    },
    methods: {
        initSt() {
            const sr = new SpeechRecognition({
                url: 'wss://nls-gateway-cn-shanghai.aliyuncs.com/ws/v1',
                appkey: 'your app key',
                token: this.token
            })
            console.warn("sr 初始化成功")
            sr.on("started", (msg) => {
                console.log("Client recv started", JSON.parse(msg))
            })

            sr.on("changed", (msg) => {
                console.log("Client recv changed:", JSON.parse(msg))
                this.srResult = JSON.parse(msg)
            })

            sr.on("completed", (msg) => {
                console.log("Client recv completed:", JSON.parse(msg))
                this.srResult = JSON.parse(msg)
            })

            sr.on("failed", (msg) => {
                console.log("Client recv failed:", JSON.parse(msg))
            })
            sr.on("closed", () => {
                console.error("sr 连接已关闭")
            })
            this.sr = sr
        },
        // 长按录音事件
        async longpressBtn(e) {
            recorderManager.start({
                duration: 600000,
                numberOfChannels: 1,
                sampleRate: 16000,
                format: "PCM",
                frameSize: 4
            })
            this.initSt()
            this.startPoint = e.touches[0];//记录长按时开始点信息,后面用于计算上划取消时手指滑动的距离。
            this.longPress = '2';
            this.VoiceText = '说话中...';
            if (!this.sr || this.srStart) {
                return
            }
            try {
                await this.sr.start(this.sr.defaultStartParams())
                this.srStart = true
            } catch (e) {
                console.log("start failed:" + e)
                return
            }

            // 监听音频开始事件
            this.sendLock = false;//长按时是不上锁的。
        },
        // 长按松开录音事件
        touchendBtn() {
            this.longPress = '1';
            this.VoiceText = '按住 说话';
            this.VoiceTitle = '松手结束录音'
            recorderManager.stop();
        },
        // 删除录音
        handleTouchMove(e) {
            //touchmove时触发
            var moveLenght = e.touches[e.touches.length - 1].clientY - this.startPoint.clientY; //移动距离
            if (Math.abs(moveLenght) > 70) {
                this.VoiceTitle = "松开手指,取消发送";
                this.VoiceText = '松开手指,取消发送';
                this.delBtn()
                this.sendLock = true;//触发了上滑取消发送,上锁
            } else {
                this.VoiceTitle = "松手结束录音";
                this.VoiceText = '松手结束录音';
                this.sendLock = false;//上划距离不足,依然可以发送,不上锁
            }
        },
        delBtn() {
            this.delShow = false;
            this.time = 0
            // this.tempFilePath = '';
            // this.VoiceTitle = '松手结束录音'
        },
    }
}
</script>

<style lang="scss">
/* 语音录制开始--------------------------------------------------------------------- */
.record-layer {
    width: 91vw;
    box-sizing: border-box;
    height: 15vw;
    position: fixed;
    margin-left: 4vw;
    z-index: 10;
    bottom: 2vh;
}

.record-layer1 {
    width: 100vw;
    box-sizing: border-box;
    height: 100vh;
    position: fixed;
    background-color: rgba(0, 0, 0, .6);
    z-index: 10;
    bottom: 0vh;
}

.record-box {
    width: 100%;
    position: relative;
}

.record-box1 {
    width: 100%;
    position: relative;
    bottom: -83vh;
    height: 17vh;
}

.record-btn-layer {
    // width: 100%;
}

.record-btn-layer button::after {
    border: none;
    transition: all 0.1s;
}

.record-btn-layer button {
    font-size: 14px;
    line-height: 40px;
    width: 100%;
    height: 40px;
    text-align: center;
    transition: all 0.1s;
}

.record-btn-layer button image {
    width: 16px;
    height: 16px;
    margin-right: 4px;
    vertical-align: middle;
    transition: all 0.3s;
}


.record-btn-layer .record-btn-2 {
    border-radius: 168rpx 168rpx 0 0;
    height: 17vh;
    line-height: 17vh;
    transition: all 0.3s;
}

/* 提示小弹窗 */
.prompt-layer {
    border-radius: 15px;
    background: #95EB6C;
    padding: 8px 16px;
    box-sizing: border-box;
    position: absolute;
    left: 50%;
    height: 11vh;
    transform: translateX(-50%);
    transition: all 0.3s;
}

.prompt-layer::after {
    content: '';
    display: block;
    border: 12px solid rgba(0, 0, 0, 0);
    border-radius: 10rpx;
    border-top-color: #95EB6C;
    position: absolute;
    bottom: -46rpx;
    left: 50%;
    transform: translateX(-50%);
    transition: all 0.3s;
}

//取消动画
.prompt-layer1 {
    border-radius: 15px;
    background: #FB5353;
    padding: 8px 16px;
    box-sizing: border-box;
    position: absolute;
    left: 50%;
    height: 11vh;
    transform: translateX(-50%);
    transition: all 0.3s;
}

.prompt-layer1::after {
    content: '';
    display: block;
    border: 12px solid rgba(0, 0, 0, 0);
    border-radius: 10rpx;
    border-top-color: #FB5353;
    position: absolute;
    bottom: -46rpx;
    left: 50%;
    transform: translateX(-50%);
    transition: all 0.3s;
}

.prompt-layer-1 {
    font-size: 12px;
    width: 150px;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    top: -400rpx;
}

.prompt-layer-1 .p {
    color: #000000;
}

.prompt-layer-1 .span {
    color: rgba(0, 0, 0, .6);
}

.prompt-loader .em {}

/* 语音音阶------------- */
.prompt-loader {
    width: 96px;
    height: 20px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 6px;
}

.prompt-loader .em {
    display: block;
    background: #333333;
    width: 1px;
    height: 10%;
    margin-right: 2.5px;
    float: left;
}

.prompt-loader .em:last-child {
    margin-right: 0px;
}

.prompt-loader .em:nth-child(1) {
    animation: load 2.5s 1.4s infinite linear;
}

.prompt-loader .em:nth-child(2) {
    animation: load 2.5s 1.2s infinite linear;
}

.prompt-loader .em:nth-child(3) {
    animation: load 2.5s 1s infinite linear;
}

.prompt-loader .em:nth-child(4) {
    animation: load 2.5s 0.8s infinite linear;
}

.prompt-loader .em:nth-child(5) {
    animation: load 2.5s 0.6s infinite linear;
}

.prompt-loader .em:nth-child(6) {
    animation: load 2.5s 0.4s infinite linear;
}

.prompt-loader .em:nth-child(7) {
    animation: load 2.5s 0.2s infinite linear;
}

.prompt-loader .em:nth-child(8) {
    animation: load 2.5s 0s infinite linear;
}

.prompt-loader .em:nth-child(9) {
    animation: load 2.5s 0.2s infinite linear;
}

.prompt-loader .em:nth-child(10) {
    animation: load 2.5s 0.4s infinite linear;
}

.prompt-loader .em:nth-child(11) {
    animation: load 2.5s 0.6s infinite linear;
}

.prompt-loader .em:nth-child(12) {
    animation: load 2.5s 0.8s infinite linear;
}

.prompt-loader .em:nth-child(13) {
    animation: load 2.5s 1s infinite linear;
}

.prompt-loader .em:nth-child(14) {
    animation: load 2.5s 1.2s infinite linear;
}

.prompt-loader .em:nth-child(15) {
    animation: load 2.5s 1.4s infinite linear;
}

@keyframes load {
    0% {
        height: 10%;
    }

    50% {
        height: 100%;
    }

    100% {
        height: 10%;
    }
}

/* 语音音阶-------------------- */
.prompt-layer-2 {
    top: -40px;
}

.prompt-layer-2 .text {
    color: rgba(0, 0, 0, 1);
    font-size: 12px;
}

/* 语音录制结束---------------------------------------------------------------- */
.flex_row {
    display: flex;
    flex-direction: row;
    gap: 10rpx
}

.record-btn-cir {
    flex-basis: 100rpx;
    border-width: 0.5px !important;
    border-color: #dadbde !important;
    border-style: solid;
}

.u_input {
    border-width: 0.5px !important;
    border-color: #dadbde !important;
    border-style: solid;
}

.record-btn-layer .record-btn-1 {
    background: #fff !important;
    // background-image: linear-gradient(to right, #43e97b 0%, #38f9d7 100%);
    color: #000000 !important;
    border-width: 0.5px !important;
    border-color: #dadbde !important;
    border-style: solid;
    border-radius: 8px;
}

.chat_list {
    padding-top: 30rpx;
    max-height: 86vh;
    overflow-y: scroll;
}

.type_right {
    margin: 30rpx 0 30rpx 50%;
}

.type_left {
    margin: 30rpx 0 30rpx 30rpx;
}
</style>

聊天组件

<template>
    <view class="box">
        <view class="qpk">{{ text }}</view>
        <view :class="type == 'right' ? 'triangle_right' : 'triangle_left'"></view>
    </view>
</template>

<script>
export default {
    props: {
        text: {
            type: String,
            default: ""
        },
        type: {
            type: String,
            default: "right"
        }
    },
    data() {
        return {
        };
    },
    watch: {},
    methods: {},
};
</script>
<style lang="scss" scoped>
// 气泡框样式
.box {
    position: relative;

    .qpk {
        width: 300rpx;
        height: 100%;
        text-align: left;
        background: #43e97b !important;
        -webkit-border-radius: 10px;
        -moz-border-radius: 10px;
        border-radius: 10px;
        word-break: break-all;
        padding: 18rpx 20rpx;
        font-size: 14px;
    }

    .triangle_left {
        position: absolute;
        height: 0px;
        width: 0px;
        border-width: 8px 18px 8px 0;
        border-style: solid;
        border-color: transparent #43e97b transparent transparent;
        top: 8rpx;
        left: -26rpx;
    }

    .triangle_right {
        position: absolute;
        height: 0px;
        width: 0px;
        border-width: 8px 0px 8px 18px;
        border-style: solid;
        border-color: transparent transparent transparent #43e97b;
        top: 8rpx;
        left: 326rpx;
    }
}
</style>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

redis数据类型和数据结构你了解吗 学习总结篇!

大家好&#xff0c;我是三叔&#xff0c;很高兴这期又和大家见面了&#xff0c;一个奋斗在互联网的打工人。 这期给大家讲一下关于 Redis 数据类型和数据结构的区别&#xff0c;很多读者包括笔者自己&#xff0c;早期也是傻傻分不清。备注&#xff1a;部分图片借鉴小林哥&…

Appium 安卓环境的配置

目录 前言&#xff1a; 环境准备 写个脚本玩玩 前言&#xff1a; 在使用Appium进行安卓自动化测试之前&#xff0c;需要配置相应的安卓环境。 环境准备 为了避免走弯路&#xff0c;我们先要确保三点&#xff1a; Android SDK API > 17 (Additional features require …

JMeter正则表达式提取器和JSON提取器基础用法,小白必会!

最近在利用JMeter做接口自动化测试&#xff0c;正则表达式提取器和JSON提取器用的还挺多&#xff0c;想着分享下&#xff0c;希望对大家的接口自动化测试项目有所启发。 在 JMeter 中&#xff0c;正则表达式和 JSON 提取器都是用于从响应数据中提取所需内容&#xff0c;但它们的…

spring复习:(39)注解方式的ProxyFactoryBean

一、定义接口 package cn.edu.tju.study.service;public interface MyService {void myMethod(); }二、定义实现类&#xff1a; package cn.edu.tju.study.service;public class MyServiceImpl implements MyService{Overridepublic void myMethod() {System.out.println(&qu…

css背景毛玻璃效果

一、结论&#xff1a;通过 css 的 backdrop-filter 属性设置滤镜函数 blur 一般会是有 背景色、透明度 的容器&#xff0c;如&#xff1a; /* 宽高等其他设置这里省略没写 */ background:rgba(3, 87, 255, 0.3); backdrop-filter: blur(10px);二、backdrop-filter 的其他用法…

ASL/CS系列音视频转换方案芯片,Typec拓展坞方案芯片

音视频单转方案芯片&#xff1a; CS5565 Typec转HDMI 8K 60HZ转换方案 可替代RTD2173 PS196 CS5801 HDMI转eDP/DP方案 可替代LT6711 CS5212 DP转VGA转换方案 可PIN TO PIN 替代RTD2166 CS5211 E…

《红蓝攻防构建实战化网络安全防御体系》读书笔记

作者&#xff1a;奇安信安服团队 ◆ 1.3 红队 各个团队在演练中的角色与分工情况如下。目标系统运营单位&#xff1a;负责红队整体的指挥、组织和协调。安全运营团队&#xff1a;负责整体防护和攻击监控工作。攻防专家&#xff1a;负责对安全监控中发现的可疑攻击进行分析和研…

vue写车牌号 自定义键盘

vue写车牌号自定义键盘 <template><div><div class"content-wrapper"><div class"content-top-wrapper"><van-radio-group v-model"radioCarType"><van-radio name"1">蓝牌<imgslot"icon…

前端学习笔记:JavaScript基础语法(ECMAScript)

此博客参考b站&#xff1a;【黑马程序员前端JavaScript入门到精通全套视频教程&#xff0c;javascript核心进阶ES6语法、API、js高级等基础知识和实战教程】https://www.bilibili.com/video/BV1Y84y1L7Nn?p76&vd_source06e5549bf018e111f4275c259292d0da 这份笔记适用于已…

ETHERNET/IP转TCP/IP网关tcp/ip协议包含哪几层

大家好&#xff0c;今天我们将带大家了解一款自主研发的通讯网关&#xff0c;远创智控YC-EIP-TCP/IP。这是一个强大的工具&#xff0c;能帮助我们将ETHERNET/IP网络和TCP/IP网络连接在一起&#xff0c;让我们更好地管理和监控网络。 1, 首先&#xff0c;让我们来看看这款网关…

Html基础知识学习——圣杯布局、margin负值、等高布局(十七)

文章目录 圣杯布局margin负值等高布局 圣杯布局 两边页面固定中间页面宽度随着浏览器大小自适应 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-widt…

Django中使用反向关系名称(related_name)解决由“多对多”关系引起的字段名字冲突问题引起的迁移命令报错。

当在模型中为关系字段添加了related_name参数后&#xff0c;您可以使用该参数指定的名称来引用反向关系。下面是一个简单的例子来说明如何引用反向关系。 假设您有以下两个模型&#xff1a; from django.db import modelsclass Author(models.Model):name models.CharField(…

十八、Unity游戏引擎入门

1、下载 首先需要下载Unity Hub,下载网址:https://unity.com/cn。 然后在其中下载Unity编辑器并安装,可选择最新版本。 接着需要选择适合的开发环境,例如Android Studio或Xcode,以便进行手机游戏开发。在安装完Unity后,需要根据项目需求下载对应的模块和插件…

php 开发微信 h5 支付 APIv3 接入超详细流程

✨ 目录 &#x1f388; 申请商户号&#x1f388; 申请商户证书&#x1f388; 设置V3密钥&#x1f388; 开通H5支付&#x1f388; 设置支付域名&#x1f388; SDK 下载&#x1f388; 第一次下载平台证书&#x1f388;非第一次下载平台证书&#x1f388; H5下单 &#x1f388; 申…

CentOS7中安装docker并配置阿里云加速器

文章目录 一、docker的安装二、docker的卸载三、配置加速器四、docker-compose安装五、docker-compose卸载六、docker-compose相关命令七、常用shell组合 一、docker的安装 参考&#xff1a;https://docs.docker.com/engine/install/centos 本文内容是基于&#xff1a;CentOS L…

管理软件开发平台:用科技提升数据治理能力,实现流程化办公!

如果实现流程化办公&#xff0c;想必是很多企业心心念念的发展愿望。但是&#xff0c;如何实现&#xff1f;利用什么样的平台可以完成这一目标&#xff1f;这是很多人值得深思的问题之一。管理软件开发平台实行100%全源码开放&#xff0c;是轻量级、可视化低代码开发平台&#…

Python Web框架 Flask 安装、使用

Python Web框架 Flask 安装 安装 Flask 框架 首先需要安装 Flask 框架, 可以通过以下命令安装: [rootlocalhost web]# pip3 install Flask Collecting FlaskDownloading Flask-2.0.3-py3-none-any.whl (95 kB)|██████████████████████████████…

【Visual Studio Code】---自定义键盘快捷键设置

概述 一个好的文章能够帮助开发者完成更便捷、更快速的开发。书山有路勤为径&#xff0c;学海无涯苦作舟。我是秋知叶i、期望每一个阅读了我的文章的开发者都能够有所成长。 一、进入键盘快捷键设置 1、进入键盘快捷键设置方法1 使用快捷键进入键盘快捷键设置先按 Ctrl K再…

基于springboot的企业员工工资管理系统(财务系统)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…