SpeechRecognition 识别 + speechSynthesis 播放,原生 API 零依赖搞定"语音对话"
在进行大模型的开发项目中,有很多会涉及到语音的识别及语音的播放。现按照 Web Speech API 原生支持的能力,实现此功能。
文章后会放源码:对接科大讯飞的版本。
<template>
<div>
<button @click="startRecognition" :disabled="isListening">开始录音</button>
<button @click="stopRecognition" :disabled="!isListening">停止</button>
<p>识别结果: {{ recognitionText }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
const isListening = ref(false);
const recognitionText = ref('');
let recognition;
const startRecognition = () => {
// 检查浏览器是否支持
if (!('webkitSpeechRecognition' in window)) {
alert('您的浏览器不支持语音识别');
return;
}
recognition = new window.webkitSpeechRecognition();
recognition.continuous = true; // 连续识别
recognition.interimResults = true; // 返回中间结果
recognition.lang = 'zh-CN'; // 设置为中文
recognition.onstart = () => {
isListening.value = true;
};
recognition.onresult = (event) => {
let interimTranscript = '';
let finalTranscript = '';
for (let i = event.resultIndex; i < event.results.length; i++) {
const transcript = event.results[i][0].transcript;
if (event.results[i].isFinal) {
finalTranscript += transcript; // 最终结果
} else {
interimTranscript += transcript; // 临时结果
}
}
recognitionText.value = finalTranscript || interimTranscript;
};
recognition.onerror = (event) => {
console.error('语音识别错误:', event.error);
isListening.value = false;
};
recognition.onend = () => {
isListening.value = false;
};
recognition.start();
};
const stopRecognition = () => {
if (recognition) {
recognition.stop();
}
};
</script>
webkitSpeechRecognition(Chrome 前缀 API)+ continuous: true 连续识别 + interimResults: true 中间结果 + lang: 'zh-CN' 中文。onresult 里区分 isFinal(最终结果)与临时结果。在识别的基础上,加入 speechSynthesis + SpeechSynthesisUtterance 实现"识别 → 文字 → 语音播放"闭环:
<template>
<div>
<button @click="startRecognition">开始语音识别</button>
<button @click="stopRecognition">停止语音识别</button>
<button @click="speakText">播放文字语音</button>
<textarea v-model="recognizedText" rows="4" cols="50" placeholder="识别结果将显示在这里"></textarea>
</div>
</template>
<script setup>
import { ref } from 'vue';
const recognizedText = ref('');
const recognition = ref(null);
const synth = ref(window.speechSynthesis);
// 初始化语音识别
const initRecognition = () => {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
recognition.value = new SpeechRecognition();
recognition.value.lang = 'zh-CN';
recognition.value.onresult = (event) => {
const transcript = event.results[0][0].transcript;
recognizedText.value = transcript;
};
recognition.value.onerror = (event) => {
console.error('语音识别出错:', event.error);
};
};
// 开始语音识别
const startRecognition = () => {
if (!recognition.value) {
initRecognition();
}
recognition.value.start();
};
// 停止语音识别
const stopRecognition = () => {
if (recognition.value) {
recognition.value.stop();
}
};
// 播放文字语音
const speakText = () => {
if (recognizedText.value) {
const utterance = new SpeechSynthesisUtterance(recognizedText.value);
utterance.lang = 'zh-CN';
synth.value.speak(utterance);
}
};
</script>
<style scoped>
button {
margin: 5px;
}
</style>
window.SpeechRecognition || window.webkitSpeechRecognition 双兜底;播放用 new SpeechSynthesisUtterance(text) 构造朗读对象,utterance.lang = 'zh-CN' 指定中文,最后 synth.speak(utterance)。国内访问可以对接百度、星火、阿里云的语音服务。作者这边对接的是科大讯飞的,感兴趣的可以注册一下科大讯飞的语音服务(demo 地址见原文)。
| 方案 | 识别(ASR) | 播放(TTS) | 网络要求 |
|---|---|---|---|
| Web Speech API(原生) | ✅ 但需科学上网 | ✅ 本地即可 | 识别依赖 Google 服务 |
| 科大讯飞 / 百度 / 星火 / 阿里云 | ✅ 国内可用 | ✅ 国内可用 | 国内直连,需注册 key |
| 术语 | 定义 |
|---|---|
| SpeechRecognition | Web Speech 语音识别接口(Chrome 下为 webkitSpeechRecognition) |
| interimResults | 是否返回中间临时识别结果(边听边出字) |
| isFinal | 标记该段结果是否为最终结果 |
| speechSynthesis | 语音合成(TTS)接口,本地朗读文字 |
| SpeechSynthesisUtterance | 一次朗读任务的描述对象(文本 + lang + 语速等) |
| ASR / TTS | 自动语音识别 / 文本转语音 |
<script setup>
import { ref } from 'vue';
const recognizedText = ref('');
const recognition = ref(null);
const synth = ref(window.speechSynthesis);
const initRecognition = () => {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
recognition.value = new SpeechRecognition();
recognition.value.lang = 'zh-CN';
recognition.value.onresult = (event) => {
recognizedText.value = event.results[0][0].transcript;
};
recognition.value.onerror = (event) => console.error(event.error);
};
const startRecognition = () => {
if (!recognition.value) initRecognition();
recognition.value.start();
};
const stopRecognition = () => recognition.value?.stop();
const speakText = () => {
if (recognizedText.value) {
const utterance = new SpeechSynthesisUtterance(recognizedText.value);
utterance.lang = 'zh-CN';
synth.value.speak(utterance);
}
};
</script>
<template>
<div>
<button @click="startRecognition">开始语音识别</button>
<button @click="stopRecognition">停止语音识别</button>
<button @click="speakText">播放文字语音</button>
<textarea v-model="recognizedText" rows="4" cols="50" placeholder="识别结果将显示在这里"></textarea>
</div>
</template>