前言
这两天逛MDN查某个API文档的时候,找到两个好玩的API,一个是语音转文字一个是文字转语音下面给大家分享一下,顺便封装一个支持语音输入的输入框。
语音转文字
说明
语音转文字主要使用了webkitSpeechRecognition
这个api,可以new一个语音识别对象,它支持以下属性配置。
属性 | 官方说明 | 实战说明 |
---|---|---|
lang | 返回并设置当前的语言 | 我用不同语言测试了一下,这个属性并不是强制的,比如设置了英文,说中文还是能识别出来,但是有些中文会被识别返回拼音。 |
continuous | 控制是为每次识别返回连续结果,还是仅返回单个结果。默认为单个(false)。 | 也就是说如果这个属性设置为false,说一段话后会自动停止识别。如果为true,会一直识别,直到调用stop或abort方法停止识别。 |
interimResults | 这个属性控制是否返回临时结果,true表示返回,false不返回。 | 如果为true,说话的时候会实时返回,如果为false,一段话结束后才返回。 |
maxAlternatives | 设置每个结果提供的最大数量。默认值为 1。 | 这个属性控制识别后返回的结果数量,如果设置为2,会返回两个识别的可能结果。 |
实战
介绍完api后,我们来实战一下,封装一个支持语音输入的输入框。
jsx
import { AudioOutlined, LoadingOutlined } from '@ant-design/icons';
import { Button, Input, Space } from 'antd';
import { useEffect, useMemo, useRef, useState } from 'react';
function App() {
const [loading, setLoading] = useState(false);
const [text, setText] = useState('');
const recognition = useRef(new window.webkitSpeechRecognition())
const lastLength = useRef(0);
useEffect(() => {
// 设置语言
recognition.current.lang = 'zh';
// 开启连续识别
recognition.current.continuous = true;
// 开启实时识别
recognition.current.interimResults = true;
function onresult(event) {
// 这个事件会把前面识别的结果都返回回来,所以需要取最后一个识别结果
const length = event.results.length;
// 没有新的识别结果的时候,事件也会触发,所以这里判断一下如果没有新的识别结果,就不取最后一个识别结果了。
if (lastLength.current === length) {
return;
}
lastLength.current = length;
console.log(event.results);
// 获取最后一个识别结果
const transcript = event.results[length - 1]?.[0]?.transcript;
// 将最后一个识别结果添加到文本
if (transcript) {
setText(text => text + transcript);
}
}
// 监听语音识别结果
recognition.current.addEventListener('result', onresult)
return () => {
if (recognition.current) {
recognition.current.removeEventListener('result', onresult)
}
}
}, [])
function click() {
if (loading) {
recognition.current.stop();
setLoading(false);
return;
}
setLoading(true);
lastLength.current = 0;
recognition.current.start();
}
const icon = useMemo(() => {
if (loading) {
return <LoadingOutlined style={{
fontSize: 16,
color: '#ffffff',
}} />
}
return <AudioOutlined
style={{
fontSize: 16,
color: '#ffffff',
}}
/>
}, [loading]);
return (
<div style={{ textAlign: 'center', marginTop: 200 }}>
<Space.Compact style={{ width: 600 }}>
<Input size='large' value={text} />
<Button
size='large'
type="primary"
onClick={click}
icon={icon}
/>
</Space.Compact>
</div>
)
}
export default App
上面就是全部代码,有个问题需要注意一下,官网例子中使用new SpeechRecognition()
这个方法new语音识别对象,但是我测试了一遍发现,google、Firefox、edge、Safari浏览器都会报错,推荐使用window.webkitSpeechRecognition
方式初始化。
兼容性
可以看到火狐浏览器是不支持的,经测试代码在火狐浏览器会报错。截图中说支持edge浏览器,经测试代码在edge中不报错,但是不返回识别结果。google和Safari浏览器可以正常使用。
效果展示
文字转语音
说明
浏览器也支持文本转语音,准确的说是读文本。这个功能主要用到了两个api,speechSynthesis
和SpeechSynthesisUtterance
,不过核心是这个SpeechSynthesisUtterance
。
SpeechSynthesisUtterance
属性 | 官方说明 |
---|---|
lang | 返回并设置当前的语言 |
pitch | 代表音调值的浮点数,默认为1,改为其他值,会发出奇怪的声音。 |
rate | 控制播放速度 |
text | 要播放的文本 |
voice | 播放的音调 |
volume | 控制声音大小 |
实战
jsx
import { Button, Input, Select, Space } from 'antd';
import { useEffect, useState } from 'react';
function App() {
const [text, setText] = useState('');
const [voice, setVoice] = useState();
const [voices, setVoices] = useState([]);
const [loading, setLoading] = useState(false);
async function getVoices() {
// 获取声音,因为这个返回值不稳定,所以加了个定时器获取,保证能返回声音类型
return new Promise(resolve => {
const timer = setInterval(() => {
const voices = window.speechSynthesis.getVoices();
if (voices?.length) {
resolve(window.speechSynthesis.getVoices());
clearInterval(timer);
}
}, 30);
})
}
useEffect(() => {
// 获取声音,并设置给下拉框
getVoices()
.then(voices => {
setVoices(voices);
});
}, []);
function click() {
const synth = window.speechSynthesis;
if (loading) {
setLoading(false);
synth.cancel();
return;
}
const utterThis = new SpeechSynthesisUtterance();
// 播放介绍
utterThis.onend = () => {
setLoading(false);
}
// 设置文本
utterThis.text = text;
// 设置语言
utterThis.lang = 'zh-CN';
// 设置声音类型
utterThis.voice = voices.find(v => v.name === voice);
// 开始播放
synth.speak(utterThis);
setLoading(true);
}
return (
<div style={{ textAlign: 'center', marginTop: 200 }}>
<Space.Compact style={{ width: 600 }}>
<Select
size='large'
value={voice}
options={voices}
style={{ width: 200 }}
onChange={(value) => setVoice(value)}
fieldNames={{ label: 'name', value: 'name' }}
/>
<Input
size='large'
value={text}
onChange={e => setText(e.target.value)}
/>
<Button
size='large'
type="primary"
onClick={click}
>
{loading ? '停止' : '播放'}
</Button>
</Space.Compact>
</div>
)
}
export default App
有个问题需要注意一下,获取声音类型的方法,第一次调用永远获取不到,第二次才能获取到,为了保证能获取到,加了个定时器一直去获取,直到成功。
兼容性
经过实战测试pc的浏览器大部分都支持。
最后
写完文章后,才发现语音转文字好像要挂代理才能正常使用,有点尴尬。写都写了,发出来吧,让大家了解一下这两个api。