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

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

语音识别页面


<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>
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
ESP8266是一款低成本、低功耗的Wi-Fi模块,可以用于连接互联网。而阿里云微信小程序是一种基于微信平台的应用程序,可以通过微信进行访问和使用。如果你想让ESP8266连接阿里云微信小程序,可以按照以下步骤进行操作: 1. 配置ESP8266:首先,你需要将ESP8266连接到你的开发板或者单片机上,并通过串口或者SPI接口与其进行通信。然后,你需要在ESP8266上安装相应的固件,例如NodeMCU或者Arduino固件。 2. 连接到Wi-Fi网络:在ESP8266上配置并连接到你的Wi-Fi网络,这样它才能够访问互联网。你可以使用ESP8266的AT指令或者编程语言(如Lua或Arduino)来实现这一步骤。 3. 注册阿里云账号:在阿里云官网上注册一个账号,并创建一个IoT平台实例。在实例中,你可以创建设备和产品,并获取相应的设备密钥和证书。 4. 编写代码:使用ESP8266的编程语言(如Lua或Arduino)编写代码,实现与阿里云IoT平台的通信。你需要使用阿里云提供的SDK或者API来实现设备与平台之间的数据传输和交互。 5. 配置微信小程序:在微信小程序开发者平台上创建一个小程序,并获取相应的AppID和AppSecret。然后,你需要在阿里云IoT平台中配置微信小程序的相关信息,包括AppID、AppSecret和回调URL等。 6. 实现数据传输:通过编程语言中的HTTP或MQTT协议,将ESP8266与阿里云IoT平台和微信小程序进行数据传输。你可以使用阿里云提供的SDK或者API来实现这一步骤。 以上是ESP8266连接阿里云微信小程序的基本步骤。具体的实现方式和代码可以根据你的需求和开发环境进行调整和修改。希望对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值