uniapp配置自定义界面百度语音识别转文字和原生使用方式

发布于:2022-12-10 ⋅ 阅读:(1746) ⋅ 点赞:(2)

前言:

在uniapp已经给出了语音识别插件配置的步骤,点击前往:语音识别插件配置

但是还是不够详细,有可能会遇到问题,所以我记录一下。


概述:

HBuilderX已支持讯飞语音识别和百度语音识别:

  • 讯飞语音识别
    由于讯飞语音识别SDK绑定appid,云端打包只能固定使用DCloud申请的appid,虽然无需开发者向讯飞语音开放平台申请应用,但也导致无法在讯飞语音开放平台自定义应用个性化的高级语音识别参数配置。讯飞不支持自定义语音识别界面

  • 注意:讯飞语音识别有识别次数限制,所以建议优先使用百度语音识别。

  • 百度语音识别
    1.需要向百度语音开放平台申请AppID、API Key、Secret Key,详情可参考百度语音识别引用申请教程。


一、百度语音识别申请、

申请链接地址:https://ai.baidu.com/


二、项目配置

打开项目的manifest.json文件,在App模块配置项中勾选百度语音识别并把上述申请的AppID、API Key、Secret Key,填入其中。


三、使用

1.自定义语音输入,我这边对其进行了一次封装,voice.vue,子组件代码

<template>
	<view class="content">
		<popup ref="popup" type="bottom" radius="6" :showCloseIcon="true" @hide="afterHide">
			<view class="voice">
				<view v-if="isShow">
					<view v-if="sendLock" class="tip">
						<view class="txt">试试这样说</view>
						<view class="txt-bt">马大云银行、马大云、马大云发展有限公司</view>
					</view>
					<view v-else>
						<view class="res-txt" @click="resultClick">
							<text :style="{
								color: (resultText == '正在识别中...' || resultText == '未检测到语音,请重试') ? 
								'#919098' : 
								'#2979ff'}">
								{{resultText}}
							</text>
							<image v-if="resultText != '正在识别中...' && resultText != '未检测到语音,请重试'"
								src="/static/img/xiaoshou.png" mode="widthFix"></image>
						</view>
					</view>
				</view>

				<view v-else class="tip">
					<view v-html="text" class="txt" style="color: #8e8d9a;"></view>

					<view v-if="!sendLock" class="prompt-loader">
						<view class="em" :style="randomRgb()" v-for="(item,index) in 30" :key="index"></view>
					</view>
					<view v-else class="prompt-loader"></view>
				</view>

				<view class="btn" @longpress="handleRecordStart" @touchmove="handleTouchMove"
					@touchend="handleRecordStop">
					<view class="btn-cont">
						<u-icon name="mic" color="#fff" size="22"></u-icon>长按开始语音搜索
					</view>
				</view>
			</view>
		</popup>
	</view>
</template>

<script>
	import env from "@/http/env.js"
	import {
		uploadOffline,
		turnWordLogin
	} from "@/http/index/index.js"
	import popup from '@/components/popup/popup.vue'
	export default {
		components: {
			popup
		},
		data() {
			return {
				recorderManager: uni.getRecorderManager(),
				voiceToken: '',
				timer: null,
				text: '',
				resultText: '正在识别中...',
				startPoint: {},
				sendLock: true,
				isShow: true,
			}
		},
		watch: {
			sendLock(newVal, oldVal) {
				console.log(newVal)
				this.recorderManager.onStop(res => {
					if (newVal) return //上锁不发送

					//解锁发送网络请求
					this.uploadAudio(res.tempFilePath)
					console.log(res.tempFilePath, '获取录音文件')
				});
			},
		},
		methods: {
			//长按录音方法
			handleRecordStart(e) {
				this.startPoint = e.touches[0]; //记录长按时开始点信息,后面用于计算上划取消时手指滑动的距离。
				this.recorderManager.start(); //开始录音
				this.text = `正在录音,<text style="color:#333">上划取消搜索</text>`;
				this.sendLock = false; //长按时不上锁。
				this.isShow = false;
				this.resultText = '正在识别中...';
			},
			//结束录音 (手指松开)时触发
			handleRecordStop(e) {
				this.isShow = true;
				this.recorderManager.stop(); //结束录音
			},
			//上划取消搜索
			handleTouchMove(e) {
				let moveLenght = e.touches[e.touches.length - 1].clientY - this.startPoint.clientY;
				if (Math.abs(moveLenght) > 50) {
					this.text = `松开手指,<text style="color:#333">取消搜索</text>`;
					this.sendLock = true; //触发了上滑取消搜索,上锁
					this.isShow = false;
				} else {
					this.text = `正在录音,<text style="color:#333">上划取消搜索</text>`;
					this.sendLock = false; //上划距离不足,可以搜索,不上锁
					this.isShow = false;
				}
			},

			//上传录音文件
			uploadAudio(tempFilePath) {
				uni.uploadFile({
					url: `${env.test.baseURL}/asr/v2/simpleRequest/`,
					filePath: tempFilePath,
					name: 'file',
					formData: { //接口上传参数
						api_key: '3yo',
						api_secret: 'zT2jXm7wCYth',
					},
					header: {
						'content-type': 'multipart/form-data',
						'Authorization': 'Basic cYmVyX3NlY3JldA',
						'Blade-Auth': uni.getStorageSync('Blade-Auth')
					},
					success: (res) => {
						if (res.statusCode != 200) {
							this.resultText = "未检测到语音,请重试";
							return;
						}

						let index = res.data.indexOf('{')
						let id = JSON.parse(res.data.substr(index)).job_id
						let params = {
							job_id: id,
							token: this.voiceToken,
						}
						this.getResult(params)
						console.log(params, 'params')
						console.log(res, 'resss')
					},
					fail: (err) => {
						this.resultText = "未检测到语音,请重试";
						console.log('---上传失败---' + JSON.stringify(err))
					}
				})
			},
			//获取录音结果
			getResult(data) {
				clearInterval(this.timer)
				uploadOffline(data).then(res => {
					if (res.data.code != 200) {
						this.resultText = "未检测到语音,请重试";
						return
					}
					
					if (res.data.job_status == 2) {
						this.timer = setInterval(() => {
							this.getResult(data)
						}, 500)
						console.log(res, '222222222222')
					} else if (res.data.job_status == 3) {
						this.resultText = res.data.result_txt || "未检测到语音,请重试";
						console.log(res, '获取录音结果')
						console.log(this.resultText, '语音文字')
					} else {
						this.$showToast(res.data.message)
					}
				})
			},
			//获取录音结果子传父
			resultClick() {
				if (this.resultText == '正在识别中...' || this.resultText == '未检测到语音,请重试') return;
				this.$emit('voiceResult', this.resultText)
			},

			//录音token
			voiceTokenMethod() {
				let params = {
					api_key: '3yo6ijgW',
					api_secret: 'zT2jXm7wCYWyrdkQjxdGGArMostbXThh',
				}
				turnWordLogin(params).then(res => {
					this.voiceToken = res.data.data.token
				})
			},
			show() {
				this.$refs.popup.show() // 显示
				this.voiceTokenMethod()
			},
			hide() {
				this.$refs.popup.hide() // 隐藏
			},
			//弹窗关闭之后的操作,点击遮罩层或关闭按钮
			afterHide() {
				this.sendLock = true;
				this.$emit('closePopup');
				clearInterval(this.timer);
				this.resultText = '正在识别中...';
			},
			randomRgb() {
				let R = Math.floor(Math.random() * 130 + 110);
				let G = Math.floor(Math.random() * 130 + 110);
				let B = Math.floor(Math.random() * 130 + 110);
				return {
					background: `rgb(${R},${G},${B}, 1)`
				};
			},
		}
	}
</script>

<style lang="scss" scoped>
	.voice {
		height: 500rpx;
		padding: 100rpx 60rpx 0 60rpx;
		position: relative;

		.res-txt {
			text-align: center;
			margin-top: 40rpx;
			font-size: 36rpx;
			color: #919098;

			image {
				display: block;
				margin: auto;
				margin-top: 10rpx;
				width: 60rpx;
				animation: bounce-down 2.6s linear infinite;
			}
		}

		.tip {
			margin-top: 15rpx;
			text-align: center;

			.txt {
				font-size: 36rpx;
				color: #151823;
			}

			.txt-bt {
				margin-top: 20rpx;
				color: #919098;
			}
		}

		.btn {
			width: 50%;
			height: 80rpx;
			display: flex;
			align-items: center;
			justify-content: center;
			color: #fff;
			border-radius: 50rpx;
			background: #3484fd;
			position: absolute;
			bottom: 50px;
			left: 50%;
			transform: translateX(-50%);

			.btn-cont {
				display: flex;
				align-items: center;
			}
		}
	}

	@-webkit-keyframes bounce-down {
		25% {
			-webkit-transform: translateY(-10px);
		}

		50%,
		100% {
			-webkit-transform: translateY(0);
		}

		75% {
			-webkit-transform: translateY(13px);
		}
	}
</style>
<style scoped>
	/* 语音动画 */
	.prompt-loader {
		width: 100%;
		height: 35px;
		display: flex;
		align-items: center;
		justify-content: space-between;
		margin: 30rpx auto;
	}

	.prompt-loader .em {
		height: 15%;
		width: 2px;
		float: left;
		display: block;
		background: #333333;
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	.prompt-loader .em:nth-child(21) {
		animation: load 1.3s 0.5s infinite linear;
	}

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

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

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

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

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

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

	.prompt-loader .em:nth-child(28) {
		animation: load 1.3s 0.1s infinite linear;
	}

	.prompt-loader .em:nth-child(29) {
		animation: load 1.3s 0.3s infinite linear;
	}

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

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

		50% {
			height: 100%;
		}

		100% {
			height: 15%;
		}
	}
</style>

2.自定义语音输入,父组件index.vue使用。

3.效果展示

效果演示


 三、第二种原生使用方式

点击直接调用百度语音原生方式

voiceBegain() {
				let options = {};
				//#ifdef APP-PLUS
				options.engine = 'baidu'; //iFly讯飞语音  baidu百度语音
				options.punctuation = false; // 是否需要标点符号 
				options.timeout = 10 * 1000; //语音录入持续时长
				plus.speech.startRecognize(options, (res) => {
					console.log(res); // 识别结果
					plus.speech.stopRecognize(); //关闭语音
				}, (err) => {
					console.log('语音识别失败:' + JSON.stringify(err));
				});
				//#endif
			},

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到