需求背景
最近在做项目需要语音转文字功能,由于我们公司购买了阿里云的语音转文字服务,所以后端的实现较为简单,只需要调用阿里云的服务即可。阿里云语音转文字文档
技术实现
- 获取音频资源
- 将音频资源上传至 sso
- 获取 sso 链接传给语音转文字接口
tip: 由于历史原因,本次只讨论wav类型的音频资源
实现历程
其实这个需求步骤非常简单,对于前端来说只要获取下音频资源,再调用下后端提供的两个接口就大功告成了。但是问题就出现在了的第一步:获取音频资源。
方案一:MediaRecorder(pass)
const startRecord = async () => {
try {
// 获取媒体权限
const stream = await navigator.mediaDevices.getUserMedia({
audio: true
})
const _recorder = new MediaRecorder(stream, {
mimeType: 'audio/wav'
})
recorderRef.current = _recorder;
// 开始录音
_recorder.start();
const chunks = [];
// 监听数据可用事件
_recorder.ondataavailable = event => {
chunks.push(event.data);
};
// 监听录音结束事件
_recorder.onstop = async () => {
setIsRecording(false)
// 🔒 关闭录音功能:停止所有音轨
if (stream) {
stream.getTracks().forEach((track) => track.stop());
}
// 将录音片段合并为一个 Blob 对象
const blob = new Blob(chunks);
const file = new File(
[blob],
'recording.wav',
{ type: 'audio/wav' }
);
const {
fileOssLink
} = (await uploadAudio(file)) ?? {};
if (fileOssLink) {
const text = await handleVoiceToText({
fileName: decodeURIComponent(fileOssLink)
})
}
};
} catch (error) {
// 处理错误
console.error('无法获取录音权限或不支持录音功能', error);
}
}
但其实const _recorder = new MediaRecorder(stream, { mimeType: 'audio/wav' }) 这行代码就会报错,我使用的浏览器是Chrome@138.0.7204.101,会如下错误
所以其实用上面这些代码是无法获取到wav格式的音频资源的。
方案二 :record.js(pass)
import Recorder from 'record.js'
const recorderRef = useRef<Recorder>();
const [isRecording, setIsRecording] = useState(false);
const startRecord = async () => {
try {
setIsRecording(true);
recorderRef.current.start();
} catch (error) {
// 处理错误
console.error('无法获取录音权限或不支持录音功能', error);
}
}
const stopRecord = () => {
recorderRef.current.stop();
recorderRef.current.exportWAV(async (blob) => {
const file = new File(
[blob],
'recording.wav',
{ type: 'audio/wav' }
);
const {
fileOssLink
} = (await uploadAudio(file)) ?? {};
if (fileOssLink) {
await handleVoiceToText({
fileName: decodeURIComponent(fileOssLink)
})
}
});
}
useEffect(() => {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
// const source = audioContext.createMediaStreamSource(stream);
recorderRef.current = new Recorder(audioContext, {
sampleRate: 16000,
numChannels: 1,
mimeType: 'audio/wav'
});
// 获取媒体权限
navigator.mediaDevices.getUserMedia({
audio: true
}).then((stream) => {
recorderRef.current.init(stream);
})
}, [])
用库解决,但是不知道怎么回事,start、stop、exportWAV这三个方法都不存在,recorderRef.current打印出的值为:
遂放弃。
方案三:js-audio-recorder (终极方案)
import Recorder from 'js-audio-recorder'
const [isRecording, setIsRecording] = useState(false);
const recorderRef = useRef<Recorder>(null);
// 停止录音时,需要手动关闭底层音频流
const supplyStopRecord = () => {
// 获取底层音频流
const stream = (recorderRef.current as any).stream as MediaStream;
// 关闭所有音轨
if (stream && stream.getTracks) {
stream.getTracks().forEach(track => {
track.stop(); // 停止每个音轨
});
}
}
const handleRecord = async () => {
const wavBlob = recorderRef.current.getWAVBlob()
const file = new File([wavBlob], 'recording.wav', { type: 'audio/wav' })
const {
fileOssLink
} = (await uploadAudio(file)) ?? {};
if (fileOssLink) {
const data = await handleVoiceToText({
fileName: decodeURIComponent(fileOssLink)
})
}
}
const startRecord = async () => {
setIsRecording(true)
recorderRef.current.start().then(
() => {
// 开始录音
console.log('开始录音了=========')
},
(error) => {
// 出错了
console.log('录音报错了====', error)
}
)
}
const stopRecording = async () => {
setIsRecording(false)
clearDownCount()
await recorderRef.current.stop()
supplyStopRecord()
await handleRecord()
}
useEffect(() => {
recorderRef.current = new Recorder({
sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
sampleRate: 16000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
numChannels: 1
})
}, [])
感兴趣可以翻看源码,我大致翻了下,包里为了获取wav格式的音频资源,做了很多专业性的音频处理。
总结
专业的事情还是交给专业的人干,发现路不通及时换一条