Web Speech API简单介绍
两个重要功能
1、语音合成(文字转语音,输入一段文本,输出语音)
2、语音识别(语音转文字,通过麦克风朗读一段文本,输处文字)
这里只测试了 谷歌,火狐,IE 3个浏览器,用的都是最新版本,
手机用的安卓,苹果,自带的浏览器测试。
简单进行了测试后结果:
1、语音合成:
3端通用,PC,安卓H5(获取不到朗读人声选项),苹果H5,
2、语音识别,只有苹果H5端可以使用
兼容性没有深入研究,只是确保PC上能进行语音合成。
使用vue3进行测试
<template>
<div id="app">
<h1>Web Speech API 演示</h1>
<div class="speech-control">
<div class="recognition-section">
<h3>语音识别</h3>
<button @click="startRecognition" :disabled="isRecognizing">
{{ isRecognizing ? '正在识别...' : '开始识别' }}
</button>
<div v-if="recognizedText" class="result">
识别结果: {{ recognizedText }}
</div>
</div>
<div class="synthesis-section">
<h3>语音合成</h3>
<div class="voice-selection">
<h4>选择语音</h4>
<div class="voice-list">
<div v-for="voice in voices" :key="voice.name" class="voice-item">
<input
type="radio"
:id="voice.name"
:value="voice"
v-model="selectedVoice"
:name="'voice'"
/>
<label :for="voice.name">
{{ voice.name }} ({{ voice.lang }})
<span v-if="voice.default" class="default-badge">默认</span>
</label>
</div>
</div>
</div>
<div class="input-group">
<input v-model="textToSpeak" placeholder="输入要朗读的文本" />
<button @click="speak" :disabled="!textToSpeak">朗读</button>
</div>
<div class="controls">
<button @click="pause">暂停</button>
<button @click="resume">继续</button>
<button @click="stop">停止</button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { speechRecognition, speechSynthesis } from '@/utils/speechService';
const isRecognizing = ref(false);
const recognizedText = ref('');
const textToSpeak = ref('');
const voices = ref([]);
const selectedVoice = ref(null);
// 加载语音列表
const loadVoices = () => {
// 获取可用的语音列表
voices.value = speechSynthesis.getVoices();
// 如果语音列表为空,等待voiceschanged事件
if (voices.value.length === 0) {
window.speechSynthesis.onvoiceschanged = () => {
voices.value = speechSynthesis.getVoices();
// 默认选择第一个中文语音
const chineseVoice = voices.value.find(voice => voice.lang.includes('zh'));
if (chineseVoice) {
selectedVoice.value = chineseVoice;
}
};
} else {
// 默认选择第一个中文语音
const chineseVoice = voices.value.find(voice => voice.lang.includes('zh'));
if (chineseVoice) {
selectedVoice.value = chineseVoice;
}
}
};
// 在组件挂载后加载语音列表
onMounted(() => {
// 添加一个小延迟,确保浏览器完全准备好
setTimeout(() => {
loadVoices();
}, 100);
});
const startRecognition = () => {
isRecognizing.value = true;
speechRecognition.start((result) => {
recognizedText.value = result;
isRecognizing.value = false;
});
};
const speak = () => {
speechSynthesis.speak(textToSpeak.value, {
lang: 'zh-CN',
pitch: 1,
rate: 1,
voice: selectedVoice.value
});
};
const pause = () => {
speechSynthesis.pause();
};
const resume = () => {
speechSynthesis.resume();
};
const stop = () => {
speechSynthesis.stop();
};
</script>
<style>
#app {
font-family: Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
h1 {
margin-bottom: 40px;
}
.speech-control {
padding: 20px;
max-width: 600px;
margin: 0 auto;
}
.recognition-section,
.synthesis-section {
margin-bottom: 30px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
.voice-selection {
margin-bottom: 20px;
}
.voice-list {
max-height: 200px;
overflow-y: auto;
border: 1px solid #eee;
border-radius: 4px;
padding: 10px;
}
.voice-item {
margin-bottom: 8px;
padding: 8px;
border-radius: 4px;
transition: background-color 0.2s;
}
.voice-item:hover {
background-color: #f5f5f5;
}
.voice-item label {
margin-left: 8px;
cursor: pointer;
}
.default-badge {
background-color: #4CAF50;
color: white;
padding: 2px 6px;
border-radius: 4px;
font-size: 12px;
margin-left: 8px;
}
.input-group {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
input[type="text"] {
flex: 1;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
padding: 8px 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:disabled {
background-color: #cccccc;
cursor: not-allowed;
}
.controls {
display: flex;
gap: 10px;
}
.result {
margin-top: 15px;
padding: 10px;
background-color: #f5f5f5;
border-radius: 4px;
}
</style>
speechService.js
// 语音识别服务
class SpeechRecognitionService {
constructor() {
this.recognition = null;
this.isSupported = 'webkitSpeechRecognition' in window || 'SpeechRecognition' in window;
if (this.isSupported) {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
this.recognition = new SpeechRecognition();
this.recognition.continuous = false;
this.recognition.interimResults = false;
this.recognition.lang = 'zh-CN';
}
}
start(callback) {
if (!this.isSupported) {
console.error('您的浏览器不支持语音识别');
return;
}
this.recognition.onresult = (event) => {
const result = event.results[0][0].transcript;
callback(result);
};
this.recognition.onerror = (event) => {
console.error('语音识别错误:', event.error);
};
this.recognition.start();
}
stop() {
if (this.recognition) {
this.recognition.stop();
}
}
}
// 语音合成服务
class SpeechSynthesisService {
constructor() {
this.synthesis = window.speechSynthesis;
this.isSupported = 'speechSynthesis' in window;
this.voices = [];
this.loadVoices();
}
loadVoices() {
if (!this.isSupported) return;
// 获取所有可用的语音
this.voices = this.synthesis.getVoices();
// 如果语音列表为空,等待voiceschanged事件
if (this.voices.length === 0) {
this.synthesis.onvoiceschanged = () => {
this.voices = this.synthesis.getVoices();
};
}
}
getVoices() {
return this.voices;
}
speak(text, options = {}) {
if (!this.isSupported) {
console.error('您的浏览器不支持语音合成');
return;
}
const utterance = new SpeechSynthesisUtterance(text);
// 设置语音参数
utterance.lang = options.lang || 'zh-CN';
utterance.pitch = options.pitch || 1;
utterance.rate = options.rate || 1;
utterance.volume = options.volume || 1;
// 设置选择的语音
if (options.voice) {
utterance.voice = options.voice;
}
this.synthesis.speak(utterance);
}
stop() {
if (this.synthesis) {
this.synthesis.cancel();
}
}
pause() {
if (this.synthesis) {
this.synthesis.pause();
}
}
resume() {
if (this.synthesis) {
this.synthesis.resume();
}
}
}
export const speechRecognition = new SpeechRecognitionService();
export const speechSynthesis = new SpeechSynthesisService();
vue