uniapp项目集成科大讯飞语音听写实现语音转文字功能

语音听写(流式版)WebAPI 文档
参考了uniapp社区插件:语音转文字,科大讯飞,实时展示文字,流示输出
参考文档:
开发参考文档
app 安卓端录音授权
【uniapp】使用permission获取录音权限及实现录音功能

要点:

由于是web端的sdk,所以uniapp中是采用renderjs实现
renderjs可以支持我们 在视图层操作dom,运行 for web 的 js库
renderjs uniapp官网解释

使用

参考uniapp插件的作者写的步骤使用就行,下面是我自己基于作者的插件稍微改一点
组件

<template>
	<view>
		<view id="record" class="record" :listeningRecordingBegins="recordFlag" :change:listeningRecordingBegins="record.listeningRecordingBeginsHandler"
			:scriptPath="scriptPath" :change:scriptPath="record.scriptPathHandler" :options="options" :change:options="record.optionsHandler"></view>
	</view>
</template>

<script>
	// import log from '../../api/log';
	export default {
		data() {
			return {
				msg: '',
				recordFlag: null,
				scriptPath: ''
			};
		},
		props: {
			options: {
				type: Object,
				default: () => {
					return {
						receordingDuration: 60,
						APPID: '',
						API_SECRET: '',
						API_KEY: ''
					};
				}
			}
		},
		created() {
			// #ifdef APP
			this.scriptPath = 'file://' + plus.io.convertLocalFileSystemURL('/');
			// #endif
		},
		methods: {
			start() {
				console.log('start', this.recordFlag);
				if (this.recordFlag == 'START') return;
				this.recordFlag = 'START';
			},
			end() {
				console.log('end', this.recordFlag);
				if (this.recordFlag == 'END') return;
				this.recordFlag = 'END';
			},
			resultMsg(e) {
				this.$emit('result', e);
				this.msg = e;
			},
			endCallback(e) {
				this.$emit('onStop', e);
				this.end();
			},
			startCallback(e) {
				this.$emit('onStart', e);
			},
			seconds(e) {
				this.$emit('countDown', e);
			},
			change(e) {
				this.$emit('change', e);
			},
		}
	};
</script>

<script lang="renderjs" module="record">
	import CryptoJS from 'crypto-js';
	let APPID = "4c12ff51";
	let API_SECRET = "ZTUxMjIyMjZiOGRjMGNhNTMwMTgwOTM1";
	let API_KEY = "f946eb15c37c18de1f7f7e2b69daba69";
	let receordingDuration = 60
	let ws = null;
	let resultText = "";
	let resultTextTemp = "";
	let timer = null;
	let tapeStatus = {
		CONNECTING: 'CONNECTING',
		OPEN: 'OPEN',
		CLOSING: 'CLOSING',
		CLOSED: 'CLOSED'
	}
	export default {
		data() {
			return {
				recorder: null,
				recorderPath: '',
			}
		},
		methods: {
			// 监听recordFlag的值的处理
			listeningRecordingBeginsHandler(flag) {
				if (flag == null) return
				if (flag == 'START') {
					this.connectWebSocket()
				} else if (flag == 'END') {
					this.recorder.stop()
				}
			},
			// 动态标签引入js
			scriptPathHandler(path) {
				var recordScript = document.getElementById("recordScript");
				if (recordScript) {
					var script = document.getElementById('recordScript');
					this.recorder = new RecorderManager(path + 'static/dist')
					console.log('有标签了', script, this.recorder);
					this.initListen()
				} else {
					var script = document.createElement('script');
					script.id = 'recordScript'
					script.src = `./static/dist/index.umd.js`;
					document.body.appendChild(script);
					script.onload = () => {
						this.recorder = new RecorderManager(path + 'static/dist')
						this.initListen()
					}
				}
			},
			optionsHandler(options) {
				APPID = options.APPID
				API_SECRET = options.API_SECRET
				API_KEY = options.API_KEY
				receordingDuration = options.receordingDuration
			},
			initListen() {
				this.recorder.onStart = () => {
					this.changeStatus(tapeStatus.OPEN);
				}
				this.recorder.onFrameRecorded = ({
					isLastFrame,
					frameBuffer
				}) => {
					if (ws.readyState === ws.OPEN) {
						ws.send(
							JSON.stringify({
								data: {
									status: isLastFrame ? 2 : 1,
									format: "audio/L16;rate=16000",
									encoding: "raw",
									audio: this.toBase64(frameBuffer),
								},
							})
						);
						if (isLastFrame) {
							this.changeStatus("CLOSING");
						}
					}

				};
				this.recorder.onStop = () => {
					clearInterval(timer);
				};
			},
			connectWebSocket() {
				const websocketUrl = this.getWebSocketUrl();
				if ("WebSocket" in window) {
					ws = new WebSocket(websocketUrl);
				} else if ("MozWebSocket" in window) {
					ws = new MozWebSocket(websocketUrl);
				} else {
					console.log("不支持WebSocket");
					return;
				}
				this.changeStatus(tapeStatus.CONNECTING);
				ws.onopen = () => {
					console.log('this.recorder', this.recorder);
					this.recorder.start({
						sampleRate: 16000,
						frameSize: 1280,
					})
					const params = {
						common: {
							app_id: APPID,
						},
						business: {
							language: "zh_cn",
							domain: "iat",
							accent: "mandarin",
							vad_eos: 5000,
							dwa: "wpgs",
						},
						data: {
							status: 0,
							format: "audio/L16;rate=16000",
							encoding: "raw",
						}
					}
					ws.send(JSON.stringify(params));
				}
				ws.onmessage = (e) => {
					console.log('---message----', e);
					this.renderResult(e.data);
				};

				ws.onerror = (e) => {
					console.error(e);
					this.recorder.stop();
					this.changeStatus(tapeStatus.CLOSED)
				};
				ws.onclose = (e) => {
					this.recorder.stop();
					this.changeStatus(tapeStatus.CLOSED)
				};
			},
			getWebSocketUrl() {
				let url = "wss://iat-api.xfyun.cn/v2/iat";
				const host = "iat-api.xfyun.cn";
				const apiKey = API_KEY;
				const apiSecret = API_SECRET;
				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;
			},
			toBase64(buffer) {
				let binary = "";
				const bytes = new Uint8Array(buffer);
				const len = bytes.byteLength;
				for (let i = 0; i < len; i++) {
					binary += String.fromCharCode(bytes[i]);
				}
				return window.btoa(binary);
			},
			renderResult(resultData) {
				let jsonData = JSON.parse(resultData);
				if (jsonData.data && jsonData.data.result) {
					let data = jsonData.data.result;
					let str = "";
					let ws = data.ws;
					for (let i = 0; i < ws.length; i++) {
						str = str + ws[i].cw[0].w;
					}
					this.$ownerInstance.callMethod('resultMsg', str || '')
					// if (data.pgs) {
					// 	if (data.pgs === "apd") {
					// 		resultText = resultTextTemp;
					// 	}
					// 	resultTextTemp = resultText + str;
					// } else {
					// 	resultText = resultText + str;
					// }
					// this.$ownerInstance.callMethod('resultMsg', resultTextTemp || resultText || '')
				}
				if (jsonData.code === 0 && jsonData.data.status === 2) {
					ws.close();
				}
				if (jsonData.code !== 0) {
					ws.close();
					console.error(jsonData);
				}
			},
			changeStatus(status) {
				let statusText = ''

				if (status === "CONNECTING") {
					statusText = '建立连接中'
					resultText = "";
					resultTextTemp = "";
				} else if (status === "OPEN") {
					statusText = '开始录音'
					this.$ownerInstance.callMethod('startCallback', {
						status,
						msg: statusText
					})
					this.countdown();
				} else if (status === "CLOSING") {
					statusText = '关闭连接中'
				} else if (status === "CLOSED") {
					statusText = "录音已关闭";
					this.$ownerInstance.callMethod('endCallback', {
						status,
						msg: statusText
					})
				}
				this.$ownerInstance.callMethod('change', {
					status,
					msg: statusText
				})
			},
			countdown() {
				let seconds = receordingDuration
				// btnControl.innerText = `录音中(${seconds}s)`;
				timer = setInterval(() => {
					seconds = seconds - 1;

					if (seconds <= 0) {
						clearInterval(timer);
						this.recorder.stop();
					} else {
						this.$ownerInstance.callMethod('seconds', seconds)
						// btnControl.innerText = `录音中(${seconds}s)`;
					}
				}, 1000);
			}
		}

	}
</script>
<style></style>

页面使用

<template>
	<view class="page-container">
		<AppNavbar navBarTitle="语音转写"></AppNavbar>
		<view class="page-content main-content">
			<view class="content">
				<!-- <button @touchstart.stop="start" @touchend.stop="end">按下说话,松开停止</button> -->
				<button @click="start">开始说话</button>
				<button @click="end">停止说话1</button>
				<view style="color:deeppink;font-size: 30rpx;">
					{{msg}}
				</view>
				<!-- 				<yimo-AudioTrans ref="yimoAudioTransRefs" :options="options" @countDown="countDown" @result="resultMsg" @onStop="onStop" @onOpen="onOpen"
					@change="change"></yimo-AudioTrans> -->

				<AudioTrans ref="yimoAudioTransRefs" :options="options" @countDown="countDown" @result="resultMsg" @onStop="onStop" @onOpen="onOpen" @change="change">
				</AudioTrans>
			</view>
		</view>
	</view>
</template>

<script>
	import AppNavbar from '@/components/AppNavbar/navbar.vue';
	import AudioTrans from '@/components/AudioTrans/AudioTrans.vue';

	export default {
		components: {
			AppNavbar,
			AudioTrans
		},
		data() {
			return {
				title: 'Hello',
				msg: '1233',
				options: {
					receordingDuration: 60,
					APPID: '4c12ff51',
					API_SECRET: 'ZTUxMjIyMjZiOGRjMGNhNTMwMTgwOTM1',
					API_KEY: 'f946eb15c37c18de1f7f7e2b69daba69'
				},
			};
		},
		onLoad() {
			uni.getSystemInfo({
				success: function(res) {
					const platform = res.platform.toLowerCase();
					if (platform === 'devtools') {
						console.log('-----浏览器环境-----');
					} else if (platform === 'ios' || platform === 'android') {
						//监听授权  
						plus.android.requestPermissions(['android.permission.RECORD_AUDIO'], function(e) {
							if (e.deniedAlways.length > 0) { //权限被永久拒绝    
								// 弹出提示框解释为何需要权限,引导用户打开设置页面开启    
								console.log('权限被永久拒绝' + e.deniedAlways.toString());
							}
							if (e.deniedPresent.length > 0) { //权限被临时拒绝    
								// 弹出提示框解释为何需要权限,可再次调用plus.android.requestPermissions申请权限    
								console.log('权限被临时拒绝' + e.deniedPresent.toString());
							}
							if (e.granted.length > 0) { //权限被允许    
								console.log('权限被允许' + e.granted.toString());
							}
						});
					} else if (platform === 'wechat') {
						console.log('-----微信环境-----');
					} else {
						console.log('-----无法确定当前环境-----');
					}
				},
				fail: function(error) {
					console.log('-----getSystemInfo error-----');
				}
			});
		},
		omShow() {},
		methods: {
			start() {
				this.$refs.yimoAudioTransRefs.start();
			},
			end() {
				this.$refs.yimoAudioTransRefs.end();
			},
			countDown(e) {
				console.log('countDown', e);
			},
			onStop(e) {
				console.log('onStop', e);
			},
			onOpen(e) {
				console.log('onOpen', e);
			},
			change(e) {
				console.log('change', e);
			},
			resultMsg(e) {
				this.msg = e;
				console.log('resultMsg', e);
			}
		}
	};
</script>

<style lang="scss" scoped>
	.main-content {
		padding-top: 10%;
	}
</style>

遇到的问题:

在浏览器端正常运行,运行到app端,一直报错 [object DOMException] at static/dist/index.umd.js:1
折磨了好久,后面才发现是安卓端没有授权的问题
授权参考:
manifest.json中加入permissions

"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",

app模块配置打开Record录音
在这里插入图片描述
页面鉴权:

			uni.getSystemInfo({
				success: function(res) {
					const platform = res.platform.toLowerCase();
					if (platform === 'devtools') {
						console.log('-----浏览器环境-----');
					} else if (platform === 'ios' || platform === 'android') {
						//监听授权  
						plus.android.requestPermissions(['android.permission.RECORD_AUDIO'], function(e) {
							if (e.deniedAlways.length > 0) { //权限被永久拒绝    
								// 弹出提示框解释为何需要权限,引导用户打开设置页面开启    
								console.log('权限被永久拒绝' + e.deniedAlways.toString());
							}
							if (e.deniedPresent.length > 0) { //权限被临时拒绝    
								// 弹出提示框解释为何需要权限,可再次调用plus.android.requestPermissions申请权限    
								console.log('权限被临时拒绝' + e.deniedPresent.toString());
							}
							if (e.granted.length > 0) { //权限被允许    
								console.log('权限被允许' + e.granted.toString());
							}
						});
					} else if (platform === 'wechat') {
						console.log('-----微信环境-----');
					} else {
						console.log('-----无法确定当前环境-----');
					}
				},
				fail: function(error) {
					console.log('-----getSystemInfo error-----');
				}
			});
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值